<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="keywords" content="玖忆;文鹤;博客">
    
    <meta name="author" content="meteor">
    <!-- preconnect -->
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    
    
    <!--- Seo Part-->
    
    <link rel="canonical" href="https://wait-you.github.io/2023/06/02/android/"/>
    <meta name="robots" content="index,follow">
    <meta name="googlebot" content="index,follow">
    <meta name="revisit-after" content="1 days">
    
        <meta name="description" content="# Android # 系统架构  # Linux 内核层  Android 平台的基础是 Linux 内核。例如： Android Runtime(ART)  依靠 Linux 内核来执行底层功能，例如线程和底层内存管理。 使用 Linux 内核可让 Android 利用主要安全功能，并且允许设备制造商为著名的内核开发硬件驱动程序  Linux 内核  Drivers ： 驱动 。 用于使某个设">
<meta property="og:type" content="article">
<meta property="og:title" content="Android">
<meta property="og:url" content="https://wait-you.github.io/2023/06/02/Android/index.html">
<meta property="og:site_name" content="玖忆">
<meta property="og:description" content="# Android # 系统架构  # Linux 内核层  Android 平台的基础是 Linux 内核。例如： Android Runtime(ART)  依靠 Linux 内核来执行底层功能，例如线程和底层内存管理。 使用 Linux 内核可让 Android 利用主要安全功能，并且允许设备制造商为著名的内核开发硬件驱动程序  Linux 内核  Drivers ： 驱动 。 用于使某个设">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://s2.loli.net/2021/12/24/SHkyPuQ3Ja9XBeI.png">
<meta property="og:image" content="http://tuchuang.wenhe9.cn/image-20210717184629920.png">
<meta property="og:image" content="http://tuchuang.wenhe9.cn/image-20210717184704648.png">
<meta property="og:image" content="http://tuchuang.wenhe9.cn/image-20210717184731513.png">
<meta property="og:image" content="http://tuchuang.wenhe9.cn/image-20210923204305535.png">
<meta property="og:image" content="http://tuchuang.wenhe9.cn/image-20210923212324220.png">
<meta property="og:image" content="http://tuchuang.wenhe9.cn/7MCnofxJWrHdXNK.png">
<meta property="og:image" content="http://tuchuang.wenhe9.cn/image-20210924182629608.png">
<meta property="og:image" content="http://tuchuang.wenhe9.cn/image-20210924182611955.png">
<meta property="og:image" content="http://tuchuang.wenhe9.cn/image-20210924183131813.png">
<meta property="og:image" content="http://tuchuang.wenhe9.cn/image-20210924184213574.png">
<meta property="og:image" content="http://tuchuang.wenhe9.cn/image-20210926173456241.png">
<meta property="og:image" content="http://tuchuang.wenhe9.cn/image-20210926164542810.png">
<meta property="og:image" content="http://tuchuang.wenhe9.cn/image-20210929190303212.png">
<meta property="og:image" content="http://tuchuang.wenhe9.cn/image-20210929195306031.png">
<meta property="og:image" content="http://tuchuang.wenhe9.cn/image-20210924192053333.png">
<meta property="og:image" content="http://tuchuang.wenhe9.cn/image-20210924212049336.png">
<meta property="article:published_time" content="2023-06-01T20:37:34.000Z">
<meta property="article:modified_time" content="2023-06-05T08:25:35.867Z">
<meta property="article:author" content="meteor">
<meta property="article:tag" content="Android">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://s2.loli.net/2021/12/24/SHkyPuQ3Ja9XBeI.png">
    
    
    <!--- Icon Part-->
    <link rel="icon" type="image/png" href="/images/wenhe.png" sizes="192x192">
    <link rel="apple-touch-icon" sizes="180x180" href="/images/wenhe.png">
    <meta name="theme-color" content="#f1404b">
    <link rel="shortcut icon" href="/images/wenhe.png">
    <!--- Page Info-->
    
    <title>
        
            Android -
        
        玖忆
    </title>
    
<link rel="stylesheet" href="/css/style.css">

    
<link rel="stylesheet" href="/assets/fonts.css">

    <!--- Font Part-->
    
    
    
    

    <!--- Inject Part-->
    
    <script id="hexo-configurations">
    let Global = window.Global || {};
    Global.hexo_config = {"hostname":"wait-you.github.io","root":"/","language":"zh-CN"};
    Global.theme_config = {"articles":{"style":{"font_size":"16px","line_height":1.5,"image_border_radius":"14px","image_alignment":"center","image_caption":false,"link_icon":true},"word_count":{"enable":true,"count":true,"min2read":true},"author_label":{"enable":true,"auto":false,"list":[]},"code_block":{"copy":true,"style":"mac","font":{"enable":false,"family":null,"url":null}},"toc":{"enable":true,"max_depth":3,"number":false,"expand":true,"init_open":true},"copyright":true,"lazyload":true,"recommendation":{"enable":false,"title":"推荐阅读","limit":3,"placeholder":"http://tuchuang.wenhe9.cn/default-bg.jpg","skip_dirs":[]}},"colors":{"primary":"#f1404b","secondary":null},"global":{"fonts":{"chinese":{"enable":false,"family":null,"url":null},"english":{"enable":false,"family":null,"url":null}},"content_max_width":"1000px","sidebar_width":"210px","hover":{"shadow":true,"scale":false},"scroll_progress":{"bar":true,"percentage":true},"busuanzi_counter":{"enable":true,"site_pv":true,"site_uv":true,"post_pv":true},"pjax":true,"open_graph":true,"google_analytics":{"enable":false,"id":null}},"home_banner":{"enable":true,"style":"static","image":{"light":"http://tuchuang.wenhe9.cn/default-bg.jpg","dark":"http://tuchuang.wenhe9.cn/default-bg.jpg"},"title":"玖忆","subtitle":{"text":["我本微末凡尘、可也心向天空"],"hitokoto":{"enable":false,"api":"https://v1.hitokoto.cn"},"typing_speed":100,"backing_speed":80,"starting_delay":500,"backing_delay":1500,"loop":true,"smart_backspace":true},"text_color":{"light":"#fff","dark":"#d1d1b6"},"text_style":{"title_size":"2.8rem","subtitle_size":"1.5rem","line_height":1.2},"custom_font":{"enable":false,"family":null,"url":null},"social_links":{"enable":true,"links":{"github":"https://gitee.com/du-jinliang","instagram":null,"zhihu":null,"twitter":null,"email":"dujinliang9@163.com"}}},"plugins":{"feed":{"enable":false},"aplayer":{"enable":false,"type":"fixed","audios":[{"name":null,"artist":null,"url":null,"cover":null}]},"mermaid":{"enable":false,"version":"9.3.0"}},"version":"2.1.4","navbar":{"auto_hide":true,"color":{"left":"#f78736","right":"#367df7","transparency":35},"links":{"Home":{"path":"/","icon":"fa-regular fa-house"}},"search":{"enable":false,"preload":true}},"page_templates":{"friends_column":2,"tags_style":"blur"},"home":{"sidebar":{"enable":true,"position":"left","first_item":"menu","announcement":null,"links":null},"article_date_format":"auto","categories":{"enable":true,"limit":3},"tags":{"enable":true,"limit":3}}};
    Global.language_ago = {"second":"%s 秒前","minute":"%s 分钟前","hour":"%s 小时前","day":"%s 天前","week":"%s 周前","month":"%s 个月前","year":"%s 年前"};
    Global.data_config = {"masonry":false};
  </script>
    
    <!--- Fontawesome Part-->
    
<link rel="stylesheet" href="/fontawesome/fontawesome.min.css">

    
<link rel="stylesheet" href="/fontawesome/brands.min.css">

    
<link rel="stylesheet" href="/fontawesome/solid.min.css">

    
<link rel="stylesheet" href="/fontawesome/regular.min.css">

    
    
    
    
<meta name="generator" content="Hexo 6.3.0"></head>


<body>
<div class="progress-bar-container">
    
        <span class="scroll-progress-bar"></span>
    

    
        <span class="pjax-progress-bar"></span>
        <span class="pjax-progress-icon">
            <i class="fa-solid fa-circle-notch fa-spin"></i>
        </span>
    
</div>


<main class="page-container">

    

    <div class="main-content-container">

        <div class="main-content-header">
            <header class="navbar-container">
    
    <div class="navbar-content">
        <div class="left">
            
            <a class="logo-title" href="/">
                
                玖忆
                
            </a>
        </div>

        <div class="right">
            <!-- PC -->
            <div class="desktop">
                <ul class="navbar-list">
                    
                        
                            <li class="navbar-item">
                                <!-- Menu -->
                                <a class="" 
                                    href="/"  >
                                    
                                        
                                            <i class="fa-regular fa-house"></i>
                                        
                                        首页
                                    
                                </a>
                                <!-- Submenu -->
                                
                            </li>
                    
                    
                </ul>
            </div>
            <!-- Mobile -->
            <div class="mobile">
                
                <div class="icon-item navbar-bar">
                    <div class="navbar-bar-middle"></div>
                </div>
            </div>
        </div>
    </div>

    <!-- Mobile drawer -->
    <div class="navbar-drawer">
        <ul class="drawer-navbar-list">
            
                
                    <li class="drawer-navbar-item flex-center">
                        <a class="" 
                        href="/"  >
                             
                                
                                    <i class="fa-regular fa-house"></i>
                                
                                首页
                            
                        </a>
                    </li>
                    <!-- Submenu -->
                    
            

        </ul>
    </div>

    <div class="window-mask"></div>

</header>


        </div>

        <div class="main-content-body">

            

            <div class="main-content">

                
                    <div class="fade-in-down-animation">
    <div class="post-page-container">
        <div class="article-content-container">

            
            
                <div class="article-title">
                    <h1 class="article-title-regular">Android</h1>
                </div>
            
                
            

            
                <div class="article-header">
                    <div class="avatar">
                        <img src="/images/wenhe.png">
                    </div>
                    <div class="info">
                        <div class="author">
                            <span class="name">meteor</span>
                            
                                <span class="author-label">Lv3</span>
                            
                        </div>
                        <div class="meta-info">
                            <div class="article-meta-info">
    <span class="article-date article-meta-item">
        <i class="fa-regular fa-pen-fancy"></i>&nbsp;
        <span class="desktop">2023-06-01 12:37:34</span>
        <span class="mobile">2023-06-01 12:37</span>
        <span class="hover-info">创建</span>
    </span>
    
        <span class="article-date article-meta-item">
            <i class="fa-regular fa-wrench"></i>&nbsp;
            <span class="desktop">2023-06-05 00:25:35</span>
            <span class="mobile">2023-06-05 00:25</span>
            <span class="hover-info">更新</span>
        </span>
    

    
        <span class="article-categories article-meta-item">
            <i class="fa-regular fa-folders"></i>&nbsp;
            <ul>
                
                    <li>
                        <a href="/categories/Android/">Android</a>&nbsp;
                    </li>
                
            </ul>
        </span>
    
    
        <span class="article-tags article-meta-item">
            <i class="fa-regular fa-tags"></i>&nbsp;
            <ul>
                
                    <li>
                        <a href="/tags/Android/">Android</a>&nbsp;
                    </li>
                
            </ul>
        </span>
    

    
    
    
    
        <span class="article-pv article-meta-item">
            <i class="fa-regular fa-eye"></i>&nbsp;<span id="busuanzi_value_page_pv"></span>
        </span>
    
</div>

                        </div>
                    </div>
                </div>
            

            <div class="article-content markdown-body">
                <h1 id="android"><a class="markdownIt-Anchor" href="#android">#</a> Android</h1>
<h2 id="系统架构"><a class="markdownIt-Anchor" href="#系统架构">#</a> 系统架构</h2>
<p><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="https://s2.loli.net/2021/12/24/SHkyPuQ3Ja9XBeI.png"
                      alt="Android 软件堆栈"
                ></p>
<h3 id="linux内核层"><a class="markdownIt-Anchor" href="#linux内核层">#</a> Linux 内核层</h3>
<ul>
<li>Android 平台的基础是 Linux 内核。例如： <code>Android Runtime(ART)</code>  依靠 Linux 内核来执行底层功能，例如线程和底层内存管理。</li>
<li>使用 Linux 内核可让 Android 利用主要安全功能，并且允许设备制造商为著名的内核开发硬件驱动程序
<ul>
<li>Linux 内核
<ul>
<li>Drivers ： 驱动 。 用于使某个设备能够正常操作和工作的程序，主要作用与硬件设备</li>
<li>Power Management ： 电源管理</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="硬件抽象层hal"><a class="markdownIt-Anchor" href="#硬件抽象层hal">#</a> 硬件抽象层（HAL）</h3>
<ul>
<li>硬件抽象层层（HAL）提供标准界面，向更高级别的 JAVA API 框架显示设备硬件功能。HAL 包含多个库模块。其中每个模块为特定类型的硬件组件实现一个界面，例如相机或蓝牙模块。当框架 API 要求访问设备硬件时，Android 将为该硬件组件加载库模块</li>
<li>只负责定义规范，不负责实现</li>
<li>Sensors 传感器。距离传感器</li>
</ul>
<h3 id="系统运行库层"><a class="markdownIt-Anchor" href="#系统运行库层">#</a> 系统运行库层</h3>
<ul>
<li><strong>Android Runtime</strong>
<ul>
<li>对于运行 Android5.0（API 级别 21）或更高版本的设备，每个应用都在其自己的进程中运行，并且有其自己的 Android Runtime (ART) 实例。ART 编写为通过执行 DEX 文件在低内存设备上运行多个虚拟机，DEX 文件是一种专为 Android 设计的字节码格式，经过优化，使用的内存很少。编译工具链（例如 Jack）将 Java 源代码编译为 DEX 字节码，使其可在 Android 平台运行</li>
<li>ART 的部分主要功能包括：
<ul>
<li>预先（AOT）和即时（JIT）编译</li>
<li>优化的垃圾回收（GC）</li>
<li>在 Android 9 （API 级别 28）及更高版本的系统中，支持将应用软件包中的 Dalvik Executable 格式 (DEX) 文件转换为更紧凑的机器代码。</li>
<li>更好的调试支持，包括专用采用分析器、详细的诊断报告和崩溃报告，并且能够设置观察点以监控特定字段</li>
</ul>
</li>
<li>在 Android 版本 5.0（API 级别 21）之前，Dalvik 是 Android Runtime。如果您的应用在 ART 上运行效果很好，那么它应该也可在 Dalvik 上运行，但反过来不一定</li>
<li>Android 还包含一套核心运行时库，可提供 Java API 框架所使用的 Java 编程语言中的大部分功能， 包括一些 Java8 语言功能</li>
</ul>
</li>
<li><strong>原生 C/C++ 库</strong>
<ul>
<li>许多核心 Android 系统组件和服务（例如 ART 和 HAL）构建自原生代码，需要以 C 和 C++ 编写的原生库。Android 平台提供 Java 框架 API 以向应用显示其中部分原生库的功能。例如，您可以通过 Android 框架的 Java OpenGL API 访问 OpenGL ES，以支持在应用中绘制和操作 2D 和 3D 图形。</li>
<li>如果开发的是需要 C 或 C++ 代码的应用，可以使用 Android NDK 直接从原生代码访问某些原生平台库。</li>
</ul>
</li>
</ul>
<h3 id="java-api-框架"><a class="markdownIt-Anchor" href="#java-api-框架">#</a> JAVA API 框架</h3>
<ul>
<li>
<p>你可以通过 JAVA 语言编写的 Android OS 的整个功能集。这些 API 形成创建 Android 应用所需的构建块，他们可以简化核心模块化系统组件和服务的重复使用，包括以下组件和服务：</p>
<ul>
<li>丰富、可拓展的视图系统，可用以构建应用的 UI，包括列表、网格、文本框、按钮甚至可嵌入的网络浏览器</li>
<li>资源管理器，用于访问非代码资源，例如本地化的字符串、图形和布局文件</li>
<li>通知管理器，可让所有应用在状态栏中显示自定义提醒</li>
<li>Activity 管理器，用于管理应用的生命周期，提供常见的导航返回栈</li>
<li>内容提供程序，可让应用访问其他应用（例如 “联系人” 应用）中的数据或者共享其自己的数据</li>
</ul>
<p>开发者可以完全访问 Android 系统应用使用的框架 API</p>
</li>
</ul>
<h3 id="系统应用"><a class="markdownIt-Anchor" href="#系统应用">#</a> 系统应用</h3>
<ul>
<li>
<p>Android 随附一套用于电子邮件、短信、日历、互联网浏览和联系人等的核心应用。平台随附的应用与用户可以选择安装的应用一样，没有特殊状态。因此第三方应用可成为用户的默认网络浏览器、短信 Messenger 甚至默认键盘（有一些例外，例如系统的 “设置” 应用）。</p>
</li>
<li>
<p>系统应用可用作用户的应用，以及提供开发者可从其自己的应用访问的主要功能。例如，如果您的应用要发短信，您无需自己构建该功能，可以改为调用已安装的短信应用向您指定的接收者发送消息。</p>
</li>
</ul>
<h2 id="应用开发特色"><a class="markdownIt-Anchor" href="#应用开发特色">#</a> 应用开发特色</h2>
<h3 id="四大组件"><a class="markdownIt-Anchor" href="#四大组件">#</a> 四大组件</h3>
<ul>
<li>Activity
<ul>
<li>是所有 Android 应用程序的门面，凡是在应用你看到的东西，都是放在 activity 中的</li>
</ul>
</li>
<li>Service
<ul>
<li>你无法看到他，但他会在后台默默的运行，即使用户退出了应用，service 仍然是可以运行的</li>
</ul>
</li>
<li>BroadcastReceiver 允许你的应用接受来自各处的广播信息，比如电话、短信等，当然你的应用也可以向外发出广播信息</li>
<li>ContentProvider
<ul>
<li>是为应用程序之间共享数据提供了可能，比如你想要好读取系统通讯录中的联系人，就需要通过 ContentProvider 来实现</li>
</ul>
</li>
</ul>
<h3 id="丰富的系统控件"><a class="markdownIt-Anchor" href="#丰富的系统控件">#</a> 丰富的系统控件</h3>
<ul>
<li>Android 系统为开发者提供了丰富的系统控件，使得我们可以很轻松的编写出漂亮的界面，当然如果你品味比较高，不满足系统自带的空间效果，完全可以定制属于自己的控件</li>
</ul>
<h3 id="sqlite数据库"><a class="markdownIt-Anchor" href="#sqlite数据库">#</a> SQLite 数据库</h3>
<ul>
<li>Android 系统还自带了这种轻量级、运算速度极快的嵌入式关系型数据库，他不仅支持标准的 SQL 语法，还可以通过 Android 封装好的 API 记性操作，让存储和读取数据变得非常方便</li>
</ul>
<h3 id="强大的多媒体"><a class="markdownIt-Anchor" href="#强大的多媒体">#</a> 强大的多媒体</h3>
<ul>
<li>Android 系统还提供了丰富的多媒体服务，如音乐、视频、录音、拍照等，这一切你都可以在程序中通过代码进行控制，让你的应用变得更加丰富多彩</li>
</ul>
<h2 id="项目结构"><a class="markdownIt-Anchor" href="#项目结构">#</a> 项目结构</h2>
<p><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://tuchuang.wenhe9.cn/image-20210717184629920.png"
                      alt="image-20210717184629920"
                ></p>
<ul>
<li><strong>.gradle 和.idea</strong>
<ul>
<li>这两个目录下放置的是都是 Android Studio 自动生成的一些文件，我们无须关心他，也不要去手动编辑</li>
</ul>
</li>
<li><strong>app</strong>
<ul>
<li>项目中的代码、资源等内容都是放置在这个目录下的，我们后面的开发工作也基本是在这个目录下进行的</li>
</ul>
</li>
<li><strong>build</strong>
<ul>
<li>这个目录重要包含了一些在编译时自动生成的文件，你也不需要过多关心</li>
</ul>
</li>
<li><strong>gradle</strong>
<ul>
<li>这个目录下包含了 gradle wrapper 的配置文件，使用 gradle wrapper 的方式不需要提前将 gradle 下载好，而是会自动根据本地的缓存情况决定是否需要联网下载 gradle。Android Studio 默认就是启动 gradle wrapper 方式的</li>
</ul>
</li>
<li><strong>.gitignore</strong>
<ul>
<li>这个文件是用来将指定的文件活目录排除在版本控制之外的</li>
</ul>
</li>
<li><strong>build.gradle</strong>
<ul>
<li>这是项目全聚德 gradle 构建脚本，通常这个文件中的内容是不需要修改的</li>
</ul>
</li>
<li><strong>gradle.properties</strong>
<ul>
<li>这个文件是全局的 gradle 配置文件，在这里配置的属性将会影响到项目中的所有的 gradle 编译脚本</li>
</ul>
</li>
<li><strong>gradlew 和 gradlew.bat</strong>
<ul>
<li>这两个文件是用来命令行界面中执行 gradle 命令的，其中 gradlew 是在 linux 活 mac 系统中使用的，gradlew.bat 是在 windows 中使用的</li>
</ul>
</li>
<li>HelloWorld.iml
<ul>
<li>iml 文件是所有 IntelliJ IDEA 项目都会自动生成的一个文件（Android Studio 是基于 IDEA 开发的），用于表示这是一个 IntelliJ IDEA 项目，我们不需要修改这个文件中的任何内容</li>
</ul>
</li>
<li><strong>local.properties</strong>
<ul>
<li>这个文件用于制定本机中的 Android SDK 路径，通常内容是自动生成的，我们并不需要修改，除非你本机中的 Android SDK 位置发生了变化，那么就将这个文件中的路径改成新的位置即可</li>
</ul>
</li>
<li><strong>settings.gradle</strong>
<ul>
<li>这个文件用于指定项目中所用引入的模块，由于 HelloWorld 项目中只有一个 app 模块，因此该文件中也就之引入了 app 这个一个模块，通常情况下，模块的引入是自动完成的，需要我们手动修改这个文件得到场景可能比较少</li>
</ul>
</li>
</ul>
<p><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://tuchuang.wenhe9.cn/image-20210717184704648.png"
                      alt="image-20210717184704648"
                ></p>
<ul>
<li><strong>build</strong>
<ul>
<li>这个目录和外层的 build 目录类似，也包含了一些在编译是自动生成的文件，不过它里面的内容会更加复杂，我们不需要过多关心</li>
</ul>
</li>
<li><strong>libs</strong>
<ul>
<li>如果你的项目中使用到了第三方 jar 包，就需要把这些 jar 包都放在 libs 目录下，放在这个目录下的 jar 包会被自动添加到项目的构建路径里</li>
</ul>
</li>
<li><strong>androidTest</strong>
<ul>
<li>此处是用来编写 Android Test 测试用例的，可以对项目进行一些自动化测试</li>
</ul>
</li>
<li><strong>java</strong>
<ul>
<li>毫无疑问，java 目录是放置我们所有 Java 代码的地方（Kotlin 代码也放在这里），展开该目录，你将看到系统帮我们自动生成了一个 MainActivity 文件</li>
</ul>
</li>
<li><strong>res</strong>
<ul>
<li>这个目录下的内容就有点多了，简单点数哦，就是你在项目中使用到的所有图片、布局、字符串等资源都要存放在这个目录下，当然这个目录下还有很多子目录
<ul>
<li>图片放在 drawable 目录下</li>
<li>布局放在 layout 目录下</li>
<li>字符串放在 values 目录下</li>
</ul>
</li>
<li>所以你不用担心会把整个 res 目录弄得乱七八糟</li>
</ul>
</li>
<li><strong>AndroidMainfest.xml</strong>
<ul>
<li>这是整个 Android 项目的配置文件，你在程序重定义的所有四大组件都需要在这个文件里面注册，另外还可以在这个文件给应用程序添加权限声明</li>
</ul>
</li>
<li><strong>test</strong>
<ul>
<li>此处是用来编写 Unit Test 测试用例的，是对项目进行自动化测试的另一种方式</li>
</ul>
</li>
<li><strong>.gitignore</strong>
<ul>
<li>这个文件用于将 app 模块内制定的目录或文件排除在版本控制之外，作用和外层的.gitignore 文件类似</li>
</ul>
</li>
<li><strong>app.iml</strong>
<ul>
<li>IntelliJ IDEA 项目自动生成的文件，我们不需要关心或修改这个文件的内容</li>
</ul>
</li>
<li><strong>build.gradle</strong>
<ul>
<li>这是 app 模块的 gradle 构建脚本，这个文件会指定很多项目构建将相关的配置</li>
</ul>
</li>
<li><strong><a class="link"   target="_blank" rel="noopener" href="http://proguard-rules.pro" >proguard-rules.pro <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a></strong>
<ul>
<li>这个文件用于指定项目代码的混淆规则，当代码开发完成以后打包成安装包问价你，如果不希望代码被别人破解，荣昌会将代码进行混淆，从而让破解者难以阅读</li>
</ul>
</li>
</ul>
<h2 id="项目中的资源"><a class="markdownIt-Anchor" href="#项目中的资源">#</a> 项目中的资源</h2>
<ul>
<li>所有以 <code>drawable</code>  开头的目录都是用来放图片的</li>
<li>所有以 <code>mipmap</code>  开头的目录都是用来放应用图标的</li>
<li>所有以 <code>values</code>  开头的目录都是用来放字符串、样式、颜色等配置的</li>
<li>所有以 <code>layout</code>  开头的目录都是用来放布局文件的</li>
</ul>
<p><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://tuchuang.wenhe9.cn/image-20210717184731513.png"
                      alt="image-20210717184731513"
                ></p>
<ul>
<li>
<p>之所以这么多的 <code>mipmap</code>  开头的目录，其实主要是为了让程序能够更好的兼容各种设备。 <code>drawable</code>  目录也是相同的道理，虽然 Android Studio 没有帮我们自动生成，但是我们也应该自己创建 drawable-hdpi, drawable-xhdpi, drawable-xxhdpi 等目录，在制作程序的时候，最好能够给同一张图片提供几个不同分辨率的版本，分别放在这些目录下，然后程序运行的时候，会自动根据当前运行设备分辨率的高低选择家在哪个目录下的图片。当然这只是理想情况，更多时候美工只会提供给我们一份图片，这是你把所有图片都放在 drawable-xxhdpi 目录下就行了，因为这是最主流的设别分辨率目录</p>
</li>
<li>
<p>下面来看下如何使用这些资源</p>
<ul>
<li>
<div class="highlight-container" data-rel="Xml"><figure class="iseeu highlight xml"><figcaption data-lang="XML"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>resources</span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="2"></td><td><pre>	<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>string</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>app_name<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>hello world<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>string</span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>resources</span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token comment"><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">  - 可以看到这里定义了一个应用程序名的字符串，我么你有以下两种方式来引用它</span><br><span class="line"></span><br><span class="line">    - 在代码中通过``R.string.app_name``可以获得该字符串的引用</span><br><span class="line">    - 在XMl中通过``@string/app_name``可以获得该字符串的引用</span><br><span class="line"></span><br><span class="line">  - 基本语法就是上面这两种方式，其中string部分是可以替换的，如果是引用的图片资源就可以替换成drawable，如果是引用的应用图标就可以替换成mipmap，如果是引用的布局文件，就可以替换策划给你layout，以此类推</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">## Activity</span><br><span class="line"></span><br><span class="line">### Activity是什么</span><br><span class="line"></span><br><span class="line">- Activity是最吸引用户的地方，它是一种可以包含用户界面的组件，主要用于和用于进行交互，一个应用程序中可以包含零个或多个Activity，但不包含Activity的应用程序很少见</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">### 手动创建Activity</span><br><span class="line"></span><br><span class="line">![image-20210717185717443](http://tuchuang.wenhe9.cn/image-20210717185717443.png)</span><br><span class="line"></span><br><span class="line">- 勾选``Generate Layout File ``表示会自动为FirstActivity创建一个对应的布局文件</span><br><span class="line"></span><br><span class="line">- 勾选``Launcher Activity``表示会自动讲FristActivity设置为当前项目的主Activity</span><br><span class="line"></span><br><span class="line">- 此时不选择上述两者，得到文件代码如下：</span><br><span class="line"></span><br><span class="line">  - ```kotlin</span><br><span class="line">    class FirstActivity : AppCompatActivity()&#123;</span><br><span class="line">        override fun onCreate(savedInstanceState : Builde?)&#123;</span><br><span class="line">            super.onCreate(SaceInstanceState)</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></div></div></span></pre></td></tr></table></figure></li>
</ul>
</li>
<li>
<p>可以看到，onCreate () 方法非常简单，就是调用了弗雷德 onCreate () 方法，当然这只是默认的实现，后面我们还需要在里面加入很多自己的逻辑</p>
</li>
</ul>
<h3 id="创建和加载布局"><a class="markdownIt-Anchor" href="#创建和加载布局">#</a> 创建和加载布局</h3>
<ul>
<li>前面我们说过，Android 程序的设计讲究逻辑和视图分离，最好每一个 Activity 都能对应一个布局。布局是用来显示界面内容的，我们现在就来手动创建一个布局文件。</li>
</ul>
<div class="highlight-container" data-rel="Xml"><figure class="iseeu highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;utf-8&quot;</span>?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">LinearLayout</span> <span class="attr">xmlns:android</span>=<span class="string">&quot;http://schemas.android.com/apk/res/android&quot;</span></span></span><br><span class="line"><span class="tag">              <span class="attr">android:orientation</span>=<span class="string">&quot;vertical&quot;</span></span></span><br><span class="line"><span class="tag">              <span class="attr">android:layout_width</span>=<span class="string">&quot;match_parent&quot;</span></span></span><br><span class="line"><span class="tag">              <span class="attr">android:layout_height</span>=<span class="string">&quot;match_parent&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">Button</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:id</span>=<span class="string">&quot;@+id/button1&quot;</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:layout_width</span>=<span class="string">&quot;wrap_content&quot;</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:layout_height</span>=<span class="string">&quot;match_parent&quot;</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:text</span>=<span class="string">&quot;@string/content&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">Button</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">LinearLayout</span>&gt;</span></span><br></pre></td></tr></table></figure></div>
<ul>
<li>
<p>其中 <code>android:id</code>  是给当前的元素定义了一个唯一的标识符，之后可以在代码中对这个元素进行操作。你可能会对 <code>@+id/button1</code>  这种语法感到陌生，但如果把加好去掉，变成 <code>@id/button1</code> ，你就会觉得熟悉了吧。这不就是 XML 中引用资源的语法吗？追不过是把 string 替换成了 id。是的，如果你需要在 XML 中引用一个 id，就是用 <code>@id/button</code>  这种语法，而如果你需要在 XML 中定义一个 id，则要使用 <code>@+id/button1</code>  这种语法。</p>
</li>
<li>
<p>在 Activity 中加载布局</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">class</span> FirstActivity <span class="token operator">:</span> <span class="token function">AppCompatActivity</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre>    <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">onCreate</span><span class="token punctuation">(</span>saveInstanceState <span class="token operator">:</span> Bundle<span class="token operator">?</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="3"></td><td><pre>        <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">onCreate</span><span class="token punctuation">(</span>saceInstanceState<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="4"></td><td><pre>        <span class="token function">setContentView</span><span class="token punctuation">(</span>R<span class="token punctuation">.</span>layout<span class="token punctuation">.</span>first_layout<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="5"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="6"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="7"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">2</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>在 <code>&lt;activity&gt;</code>  标签中，我们使用了 <code>andoird:name</code>  来指定具体注册哪一个 <code>Activity</code> ，那么这里填入的 <code>.FristActivity</code>  是什么意思呢，其实这不过是 <code>cn.wenhe9.helloworld.FristActivity</code>  的缩写而已，由于在最外层的 <code>&lt;manifest&gt;</code>  标签中已经通过 <code>package</code>  属性智定乐程序的包名是 <code>cn.wenhe9.helloworld</code> ，因此咋注册 Actitivy 时，这一部分可以省略，直接使用 <code>.FristActivity</code>  即可</p>
</li>
<li>
<p>为程序配置主 <code>Activity</code> ，在 <code>&lt;activity&gt;</code>  标签的内容部加入 <code>&lt;intent-filter&gt;</code>  标签，并在这个标签里添加 <code>&lt;action android:name=&quot;android.intent.action.MAIN&quot;/&gt;</code>  和 <code>&lt;category android:name=&quot;android.intent.category.LAUNCHER&quot;/&gt;</code>  这两句声明即可</p>
</li>
<li>
<p>除此之外，我们还可以使用 <code>android:label</code>  指定 Activity 中标题栏中的内容，标题栏是显示在 Activity 最顶部的，待会儿运行的时候，你就会看到，需要注意的是，给主 Activity 指定的 label 不仅会成为标题栏中的内容，还会成为启动器 (Launcher) 中应用程序显示的名称</p>
</li>
<li>
<p>如果你的应用程序中没有声明任何一个人 Activity 作为主 Activity，这个程序仍然是可以正常安装的，只是你无法在启动器中看到或者打开这个程序，这种程序一般是作为第三方服务供其他应用在内部进行调用的、</p>
</li>
</ul>
<h3 id="在activity中使用toast"><a class="markdownIt-Anchor" href="#在activity中使用toast">#</a> 在 Activity 中使用 Toast</h3>
<ul>
<li>
<p>Toast 是 ANdroid 系统提供的一种非常好的提醒方式，在程序中可以使用他将一些短小的信息通知给用户，这些信息会在一段时间后自动消失，并且不会占用任何屏幕空间没我们现在就尝试一下如果在 Activity 中使用 Toast</p>
</li>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">class</span> FirstActivity <span class="token operator">:</span> <span class="token function">AppCompatActivity</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre>    <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">onCreate</span><span class="token punctuation">(</span>saveInstanceState <span class="token operator">:</span> Bundle<span class="token operator">?</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="3"></td><td><pre>        <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">onCreate</span><span class="token punctuation">(</span>saceInstanceState<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="4"></td><td><pre>        <span class="token function">setContentView</span><span class="token punctuation">(</span>R<span class="token punctuation">.</span>layout<span class="token punctuation">.</span>first_layout<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="5"></td><td><pre>        <span class="token keyword">val</span> button1 ： Button <span class="token operator">=</span> <span class="token function">findViewById</span><span class="token punctuation">(</span>R<span class="token punctuation">.</span>id<span class="token punctuation">.</span>button1<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="6"></td><td><pre>        button1<span class="token punctuation">.</span><span class="token function">setOnClickListener</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="7"></td><td><pre>            Toast<span class="token punctuation">.</span><span class="token function">makeTest</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">,</span> <span class="token string-literal singleline"><span class="token string">"你点击了一个按钮"</span></span><span class="token punctuation">,</span> Toast<span class="token punctuation">.</span>LENGTH_SHORT<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">show</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="8"></td><td><pre>        <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="9"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="10"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="11"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">3</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
<li>
<p>这里我们创建了两个菜单项，其中 <code>&lt;item&gt;</code>  标签用来创建具体的某一个菜单项，然后通过 <code>androidLid</code>  给这个菜单项制定一个唯一的标识符，通过 <code>android:title</code>  给这个菜单项制定一个名称</p>
</li>
<li>
<p>接着回到 <code>FirstActivity</code>  中重写 <code>onCreateOptionsMenu()</code>  方法</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">onCreateOptionsMenu</span><span class="token punctuation">(</span>menu<span class="token operator">:</span> Menu<span class="token operator">?</span><span class="token punctuation">)</span><span class="token operator">:</span> Boolean <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre>        menuInflater<span class="token punctuation">.</span><span class="token function">inflate</span><span class="token punctuation">(</span>R<span class="token punctuation">.</span>menu<span class="token punctuation">.</span>main<span class="token punctuation">,</span> menu<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="3"></td><td><pre>        <span class="token keyword">return</span> <span class="token boolean">true</span></pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="5"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">4</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
</ul>
<h3 id="销毁一个activity"><a class="markdownIt-Anchor" href="#销毁一个activity">#</a> 销毁一个 Activity</h3>
<ul>
<li>
<p>按一下 back 键</p>
</li>
<li>
<p>调用 <code>finish()</code></p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre>button1<span class="token punctuation">.</span><span class="token function">setOnClickListener</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre>    <span class="token function">finish</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">5</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>首先构建了一个 Intent 对象，第一个参数传入 this，也就是 FirstActivity 作为上下文，第二个参数传入 <code>SecondActivity::class.java</code>  作为目标 Activity，这样我们 “意图 “就非常明显了。注意，Kotlin 中 <code>SecondActivity::class.java</code>  法就相当于 Java 中的 <code>SecondActivity.class</code>  的写法</p>
</li>
</ul>
<h4 id="使用隐式intent"><a class="markdownIt-Anchor" href="#使用隐式intent">#</a> 使用隐式 Intent</h4>
<ul>
<li>
<p>相对于显示 Intent，隐式 Intent 则含蓄了很多，他并不明确的指出想要启动哪一个 Activity，而是指定了一系列更为抽象的 action 和 category 等信息，然后交由系统去分析这个 Intent，并帮我们找出合适的 Activity 去启动</p>
</li>
<li>
<p>什么叫做合适的 Activity？简单来说就是可以响应这个隐式 Intent 的 Activity</p>
<ul>
<li>
<div class="highlight-container" data-rel="Xml"><figure class="iseeu highlight xml"><figcaption data-lang="XML"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>activity</span> <span class="token attr-name"><span class="token namespace">android:</span>name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>.SecondActivity<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="2"></td><td><pre>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>intent-filter</span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="3"></td><td><pre>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>action</span> <span class="token attr-name"><span class="token namespace">android:</span>name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>cn.wenhe9.testIntent<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span></pre></td></tr><tr><td data-num="4"></td><td><pre>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>category</span> <span class="token attr-name"><span class="token namespace">android:</span>name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>android.intent.category.DEFAULT<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span></pre></td></tr><tr><td data-num="5"></td><td><pre>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>intent-filter</span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="6"></td><td><pre><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>activity</span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="7"></td><td><pre><span class="token comment"><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">- ```kotlin</span><br><span class="line">  val intent = Intent(&quot;cn.wenhe9.testIntent&quot;)</span><br><span class="line">  startActivity(intent)</span><br></pre></td></tr></table></figure></div></div></span></pre></td></tr></table></figure></li>
</ul>
</li>
<li>
<p>满足 action 和 category 即可， <code>DEFAULT</code>  是默认的分类，在 activity 可以不去指定，指定的话，代码为:</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">val</span> intent <span class="token operator">=</span> <span class="token function">Intent</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"cn.wenhe9.testIntent"</span></span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="2"></td><td><pre>intent<span class="token punctuation">.</span><span class="token function">addCategory</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"cn.wenhe9.category"</span></span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token function">startActivity</span><span class="token punctuation">(</span>intent<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">7</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>可以在 <code>&lt;intent-filter&gt;</code>  标签中再配置一个 <code>&lt;data&gt;</code>  标签，用于更精确的指定当前 Activity 能够响应的数据， <code>&lt;data&gt;</code>  标签中主要可以配置以下内容:</p>
<ul>
<li><code>android:scheme</code>
<ul>
<li>用于指定数据的协议部分，如上例的 https 部分</li>
</ul>
</li>
<li><code>andorid:host</code>
<ul>
<li>用于指定数据的主机名部分，如上例的 <code>www.baiduc.com</code>  部分</li>
</ul>
</li>
<li><code>andorid:port</code>
<ul>
<li>用于指定数据的端口部分，一般紧随在主机名之后</li>
</ul>
</li>
<li><code>andorid:path</code>
<ul>
<li>用于指定主机名和端口之后的部分，如一段网址中跟在域名之后的内容</li>
</ul>
</li>
<li>``andorid:mimeType`
<ul>
<li>用于指定可以处理的数据类型，允许使用通配符的方式进行指定</li>
</ul>
</li>
</ul>
</li>
<li>
<p>只有当 <code>&lt;data&gt;</code>  标签中指定的内容和 Intent 中携带的 Data 完全一致时，当前的 Activity 才能够响应该 intent, 不过在 <code>&lt;data&gt;</code>  标签中一般不会指定过多的内容，例如在上面的浏览器示例中，其实只需要指定 <code>andorid:scheme</code>  为 <code>https</code> ，就可以响应所有 <code>https</code>  协议的 intent 了</p>
<ul>
<li>
<div class="highlight-container" data-rel="Xml"><figure class="iseeu highlight xml"><figcaption data-lang="XML"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>activity</span> <span class="token attr-name"><span class="token namespace">android:</span>name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>.ThirdActivity<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="2"></td><td><pre>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>intent-filter</span> <span class="token attr-name"><span class="token namespace">tool:</span>ignore</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>AppLinkUrlError<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="3"></td><td><pre>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>action</span> <span class="token attr-name"><span class="token namespace">android:</span>name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>android.intent.action.VIEW<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span></pre></td></tr><tr><td data-num="4"></td><td><pre>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>category</span> <span class="token attr-name"><span class="token namespace">android:</span>name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>android.intent.category.DEFAULT<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span></pre></td></tr><tr><td data-num="5"></td><td><pre>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>data</span> <span class="token attr-name"><span class="token namespace">android:</span>scheme</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span></pre></td></tr><tr><td data-num="6"></td><td><pre>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>intent-filter</span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="7"></td><td><pre><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>activity</span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="8"></td><td><pre><span class="token comment"><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">  - ![image-20210923203520016](http://tuchuang.wenhe9.cn/image-20210923203520016.png)</span><br><span class="line"></span><br><span class="line">- 除了``https``协议外，我们还可以指定很多其他协议，比如geo表示地理位置，tel表示拨打电话</span><br><span class="line"></span><br><span class="line">  - ```kotlin</span><br><span class="line">    button1.setOnClickListener&#123;</span><br><span class="line">        val intent = Intent(Intent.ACTION_DIAL)</span><br><span class="line">        intent.data = Uri.parse(&quot;tel:10086&quot;)</span><br><span class="line">        startActivity(intent)</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></div></div></span></pre></td></tr></table></figure></li>
<li>
<p><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://tuchuang.wenhe9.cn/image-20210923204305535.png"
                      alt="image-20210923204305535"
                ></p>
</li>
</ul>
</li>
</ul>
<h4 id="向下一个activity传递数据"><a class="markdownIt-Anchor" href="#向下一个activity传递数据">#</a> 向下一个 Activity 传递数据</h4>
<ul>
<li>
<p>Intent 中提供了一系列的 <code>putExtra()</code>  方法的重载，可以把我们想要传递的数据暂存在 Intent 中，在启动另一个 Activity 后，只需要把这些数据从 Intent 中取出就可以了。在这里，第一个参数是键，用于之后从 Intent 中取值，第二个参数才是真正要传递的数据</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre>button1<span class="token punctuation">.</span><span class="token function">setOnclickListener</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre>    <span class="token keyword">val</span> <span class="token keyword">data</span> <span class="token operator">=</span> <span class="token string-literal singleline"><span class="token string">"hello world"</span></span></pre></td></tr><tr><td data-num="3"></td><td><pre>    <span class="token keyword">val</span> intent <span class="token operator">=</span> <span class="token function">Intent</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">,</span> SecondActivity<span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">.</span>java<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="4"></td><td><pre>    intent<span class="token punctuation">.</span><span class="token function">putExtra</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"extra_data"</span></span><span class="token punctuation">,</span> <span class="token keyword">data</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="5"></td><td><pre>    <span class="token function">startActivity</span><span class="token punctuation">(</span>intent<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="6"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="7"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">9</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
</ul>
<h4 id="返回数据给上一个activity"><a class="markdownIt-Anchor" href="#返回数据给上一个activity">#</a> 返回数据给上一个 Activity</h4>
<ul>
<li>
<p>返回上一个 Activity 和返回数据给下一个 Activity 不同，返回上一个 Activity 只需要按一下 Back 键就可以了，并没有一个用于启动 Activity 的 intent 来传递数据，Activity 类中有一个用于启动 Activity 的 <code>startActivityForResult()</code>  方法，但他期望在 Activity 销毁的时候能够返回一个结果给上一个 Activity</p>
</li>
<li>
<p><code>startActivityForResult</code>  方法接收两个参数，第一个参数还是 intent，第二个参数是请求吗，用于在之后的回调中判断数据的来源，请求码需要是一个唯一的</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre>button1<span class="token punctuation">.</span><span class="token function">setOnclickListener</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre>    <span class="token keyword">val</span> intent <span class="token operator">=</span> <span class="token function">Intent</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">,</span> SencondActivity<span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">.</span>java<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="3"></td><td><pre>	<span class="token function">startActivityForResult</span><span class="token punctuation">(</span>intent<span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="5"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">10</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>第二个代码中，Intent 只是用于传递数据，没有指定任何意图，把要传递的数据存放在 Intent 中，然后调用了 <code>SetResult()</code>  方法，这个方法用于向上一个 Activity 返回数据，这个方法接收两个参数，第一个参数用于向上一个 Activity 返回处理结果，一般只使用 <code>RESULT_OK</code>  或 <code>RESULT_CANCELED</code>  这两个值；第二个参数则把带有数据的 Intent 传递回去。最后调用 ```finish ()`` 方法来销毁当前 Activity</p>
</li>
<li>
<p>因为最后使用 <code>startAcitivityForResult</code>  方法来启动 SecondACtivity 的，在 SecondActivity 被销毁之后，会回调上一个 Activity 的 <code>onAcitivityResult()</code>  方法</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">onActivityResult</span><span class="token punctuation">(</span>requestCode<span class="token operator">:</span> Int<span class="token punctuation">,</span> resultCode<span class="token operator">:</span> Int<span class="token punctuation">,</span> <span class="token keyword">data</span><span class="token operator">:</span> Intent<span class="token operator">?</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre>    <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">onActivityResult</span><span class="token punctuation">(</span>requestCode<span class="token punctuation">,</span> resultCode<span class="token punctuation">,</span> <span class="token keyword">data</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="3"></td><td><pre></pre></td></tr><tr><td data-num="4"></td><td><pre>    <span class="token keyword">when</span><span class="token punctuation">(</span>requestCode<span class="token punctuation">)</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="5"></td><td><pre>        <span class="token number">1</span> <span class="token operator">-></span> <span class="token keyword">if</span> <span class="token punctuation">(</span>resultCode <span class="token operator">==</span> Activity<span class="token punctuation">.</span>RESULT_OK<span class="token punctuation">)</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="6"></td><td><pre>            <span class="token keyword">val</span> returnData <span class="token operator">=</span> <span class="token keyword">data</span><span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">getStringArrayExtra</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"data_return"</span></span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="7"></td><td><pre>            Log<span class="token punctuation">.</span><span class="token function">d</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"FirstActivity"</span></span><span class="token punctuation">,</span> <span class="token string-literal singleline"><span class="token string">"returned data is  </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">$</span><span class="token expression">returnData</span></span><span class="token string">"</span></span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="8"></td><td><pre>        <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="9"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="10"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="11"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">11</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
</ul>
<h3 id="activity的生命周期"><a class="markdownIt-Anchor" href="#activity的生命周期">#</a> Activity 的生命周期</h3>
<h4 id="返回栈"><a class="markdownIt-Anchor" href="#返回栈">#</a> 返回栈</h4>
<ul>
<li>Android 是使用任务（Task）来管理 Activity 的，一个任务就是一组存放在栈里的 Activity 的集合，这个栈也被称为 <code>返回栈（back stack）</code> 。栈是一种后进先出的数据结构，在默认情况下，每当我们启动了一个新的 Activity，他就会在返回栈中入栈，并处于栈顶的位置，而每当我们按下 Back 键或调用 <code>finish()</code>  方法去销毁一个 Activity 时，处于栈顶的 Activity 就会出栈，前一个入栈的 Activity 就会重新处于栈顶的位置，系统总是显示处于栈顶的 Activity 给用户</li>
<li><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://tuchuang.wenhe9.cn/image-20210923212324220.png"
                      alt="image-20210923212324220"
                ></li>
</ul>
<h4 id="activity状态"><a class="markdownIt-Anchor" href="#activity状态">#</a> Activity 状态</h4>
<ul>
<li>
<p>每个 Activity 在其生命周期中最多可能会有四种状态</p>
</li>
<li>
<p><strong>运行状态</strong></p>
<ul>
<li>当一个 Activity 位于返回栈的栈顶时，Activity 就处于运行状态。系统最不愿意回收的就是处于运行状态的 Activity</li>
</ul>
</li>
<li>
<p><strong>暂停状态</strong></p>
<ul>
<li>当一个 Activity 不再处于栈顶位置，但仍然可见时，Activity 就进入了暂停状态。</li>
<li>为什么既然 Activity 已经不在栈顶了，怎么会可见呢？
<ul>
<li>这是因为并不是每一个 Activity 都会占满整个屏幕，比如对话框形式的 Activity 只会占用屏幕中间的部分区域</li>
</ul>
</li>
<li>处于暂停状态的 Activity 仍然是完全存活着的，系统也不愿意回收这种 Activity（因为他还是可见的，回收可见的东西都会在用户体验方面有不好的影响），只有在内存极低的情况下，系统才会考虑回收这种 Activity</li>
</ul>
</li>
<li>
<p><strong>停止状态</strong></p>
<ul>
<li>当一个 Activity 不再处于栈顶位置，并且完全不可见的时候，就进入了停止状态， 系统仍然会为这种 Activity 保存相应的状态和成员变量，但是这并不是完全可靠的，当其他地方需要内存时，处于停止状态的 Activity 有可能会被系统回收</li>
</ul>
</li>
<li>
<p><strong>销毁状态</strong></p>
<ul>
<li>一个 Activity 从返回栈中移除后就变成了销毁状态。系统最倾向于回收这种状态的<br>
 Activity，以保证手机的内存充足</li>
</ul>
</li>
</ul>
<h4 id="activity的生存期"><a class="markdownIt-Anchor" href="#activity的生存期">#</a> Activity 的生存期</h4>
<ul>
<li>Activity 类中定义了 7 个回调方法，覆盖了 Activity 生命周期的每一个环节
<ul>
<li>onCreate()
<ul>
<li>他会在 Activity 第一次被创建的时候调用，在这个方法中完成 Activity 的初始化操作，比如加载布局，绑定事件等</li>
</ul>
</li>
<li>onStart()
<ul>
<li>这个方法在 Activity 由不可见变为可见的时候调用</li>
</ul>
</li>
<li>onResume()
<ul>
<li>这个方法在 Activity 准备好和用户进行交互的时候调用，此时的 Activity 一定位于返回栈的栈顶，并且处于运行状态</li>
</ul>
</li>
<li>onPause()
<ul>
<li>这个方法在系统准备去启动或者恢复另一个 Activity 的时候调用，我们通常会在这个方法中将一些消耗 CPU 的资源释放掉，以及保存一些关键数据，但这 个方法的执行速度一定要快，不然会影响到新的栈顶 Activity 的使用</li>
</ul>
</li>
<li>onStop()
<ul>
<li>这个方法在 Activity 完全不可见的时候调用，他和 <code>onPause()</code>  方法的区别在于，如果启动的新 Activity 是一个对话框式的 Activity，那么 <code>onPause</code>  方法会得到执行，而 <code>onStop()</code>  方法并不会执行</li>
</ul>
</li>
<li>onDestroy()
<ul>
<li>这个方法在 Activity 被销毁之前调用，之后 Activity 的状态将变为销毁状态</li>
</ul>
</li>
<li>onRestart()
<ul>
<li>这个方法在 Activity 由停止状态变为运行状态之前调用，也就是 Activity 被重新启动了</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>以上方法除了 <code>onRestart</code>  方法，其他都是两两相对的，从而又可以将 Activity 分为一下三种生存期</p>
<ul>
<li>完整生存期
<ul>
<li>Activity 在 onCreate () 方法和 onDestroy () 方法之间所经历的就是完整生存期。一般情况下，一个 Activity 会在 onCreate () 方法中完成各种初始化操作，而在 onDestroy () 方法中完成释放内存的操作。</li>
</ul>
</li>
<li>可见生存期
<ul>
<li>Activity 在 onStart () 方法和 onStop () 方法之间所经历的就是可见生存期。在可见生存期内，Activity 对于用户总是可见的，即便有可能无法和用户进行交互。我们可以通过这两个方法合理地管理那些对用户可见的资源。比如在 onStart () 方法中对资源进行加载，而在 onStop () 方法中对资源进行释放，从而保证处于停止状态的 Activity 不会占用过多内存。</li>
</ul>
</li>
<li>前台生存期
<ul>
<li>Activity 在 onResume () 方法和 onPause () 方法之间所经历的就是前台生存期。在前台生存期内，Activity 总是处于运行状态，此时的 Activity 是可以和用户进行交互的，我们平时看到和接触最多的就是这个状态下的 Activity。</li>
</ul>
</li>
</ul>
<p><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://tuchuang.wenhe9.cn/7MCnofxJWrHdXNK.png"
                      alt="image-20210923223847936"
                ></p>
<h4 id="activity被回收的数据保存"><a class="markdownIt-Anchor" href="#activity被回收的数据保存">#</a> Activity 被回收的数据保存</h4>
<ul>
<li>
<p>使用 <code>onSaveInstanceState()</code>  方法，这个方法保证在 Activity 被回收之前一定会被调用， <code>onSaveInstanceState()</code>  方法会携带一个 Bundle 类型额参数，Bundle 提供了一系列用于保存数据的方法，和之前的 intent 语法一样</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">onSaveInstanceState</span><span class="token punctuation">(</span>outState<span class="token operator">:</span> Bundle<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre>    <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">onSaveInstanceState</span><span class="token punctuation">(</span>outState<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="3"></td><td><pre>    <span class="token keyword">val</span> tempData <span class="token operator">=</span> <span class="token string-literal singleline"><span class="token string">"something you just typed"</span></span></pre></td></tr><tr><td data-num="4"></td><td><pre>    outState<span class="token punctuation">.</span><span class="token function">putString</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"data_key"</span></span><span class="token punctuation">,</span> tempData<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="5"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="6"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">12</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
</ul>
<h3 id="activity的启动模式"><a class="markdownIt-Anchor" href="#activity的启动模式">#</a> Activity 的启动模式</h3>
<ul>
<li>启动模式一共有四种，分别是 <code>standard</code> 、 <code>singleTop</code> ， <code>singleTask</code>  和 <code>singleInstance</code> ，可以在 <code>AndroidManifset.xml</code>  中通过给 <code>&lt;activity&gt;</code>  标签指定能够 <code>android:launchModel</code>  属性来选择启动模式</li>
</ul>
<h4 id="standard"><a class="markdownIt-Anchor" href="#standard">#</a> <strong>standard</strong></h4>
<ul>
<li><code>standard</code>  是 Activity 默认的启动模式，在不进行显示指定的情况下，所有 Activity 都会自动使用这种模式</li>
<li>在 <code>standard</code>  模式下，每当启动一个新的 Activity，他就会在返回栈中入栈，并处于栈顶的位置，对于使用 <code>standard</code>  模式的 Activity，系统不会在乎这个 Activity 是否已经在返回栈中存在，每次启动都会创建一个 Activity 的新实例</li>
<li><code>standard</code>  模式的原理如图：
<ul>
<li><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://tuchuang.wenhe9.cn/image-20210924182629608.png"
                      alt="image-20210924182629608"
                ></li>
</ul>
</li>
</ul>
<h4 id="singletop"><a class="markdownIt-Anchor" href="#singletop">#</a> <strong>singleTop</strong></h4>
<ul>
<li>当 Activity 的启动模式指定为 <code>singleTop</code> ，在启动 Activity 时如果发现返回栈的栈顶已经是该 Activity，则认为可以直接使用它，不会创建新的 Activity 实例</li>
<li><code>singleTop</code>  模式的原理如图：
<ul>
<li><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://tuchuang.wenhe9.cn/image-20210924182611955.png"
                      alt="image-20210924182611955"
                ></li>
</ul>
</li>
</ul>
<h4 id="singletask"><a class="markdownIt-Anchor" href="#singletask">#</a> singleTask</h4>
<ul>
<li>
<p>当 Activity 的启动模式指定为 <code>singleTask</code> ，每次启动该 Activity 时，系统首先会在烦恼会展中检查是否存在该 Activity 的实例，如果发现已经存在则直接使用该实例，并把在这个 Activity 之上的所有其他 Activity 统统出栈，如果没有发现就会创建一个新的 Activity 的实例</p>
</li>
<li>
<p><code>singleTask</code>  模式的原理如图：</p>
<ul>
<li><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://tuchuang.wenhe9.cn/image-20210924183131813.png"
                      alt="image-20210924183131813"
                ></li>
</ul>
</li>
</ul>
<h4 id="singleinstance"><a class="markdownIt-Anchor" href="#singleinstance">#</a> singleInstance</h4>
<ul>
<li>在 <code>singleInstance</code>  模式下，会有一个单独的返回栈来管理这个 Activity，不管是哪个应用程序来访问这个 Activity，都共用一个返回栈。</li>
<li>在一个前三种模式的 Activity 调用一个 <code>singleInstance</code>  模式的 Activity，又在或者调用一个前者模式的 Activity，那么第一个和第三个 Activity 是在同一个返回栈里，第二个是在一个单独的返回栈中，当在第三个 Activity 点击了 Back 按钮后，会直接从第三个回到的第一个 Activity，这是因为在这个返回栈里只有一和三，三出栈后就是一了，当在一又点击了 Back 后，会进入二的 Activity，这是因为在一和三的返回栈里在一出栈后已经没有 Activity 了，于是就显示另一个返回栈的栈顶 Activity，最后再按下 back 键，这时所有的返回栈都为空了，也就自然的退出程序了</li>
<li><code>singleInstance</code>  模式的原理如图:
<ul>
<li><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://tuchuang.wenhe9.cn/image-20210924184213574.png"
                      alt="image-20210924184213574"
                ></li>
</ul>
</li>
</ul>
<h2 id="fragment"><a class="markdownIt-Anchor" href="#fragment">#</a> Fragment</h2>
<h3 id="静态添加fragment"><a class="markdownIt-Anchor" href="#静态添加fragment">#</a> 静态添加 Fragment</h3>
<ol>
<li>
<p>编写布局文件</p>
</li>
<li>
<p>编写自定义 Fragment 类继承自 Fragment，在 onCreateView 中加载布局</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">class</span> TestFragment <span class="token operator">:</span> <span class="token function">Fragment</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre>    <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">onCreateView</span><span class="token punctuation">(</span>inflater<span class="token operator">:</span> LayoutInflater<span class="token punctuation">,</span> container<span class="token operator">:</span> ViewGroup<span class="token operator">?</span><span class="token punctuation">,</span> savedInstanceState<span class="token operator">:</span> Bundle<span class="token operator">?</span><span class="token punctuation">)</span><span class="token operator">:</span> View<span class="token operator">?</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="3"></td><td><pre>        <span class="token keyword">return</span> inflater<span class="token punctuation">.</span><span class="token function">inflate</span><span class="token punctuation">(</span>R<span class="token punctuation">.</span>layout<span class="token punctuation">.</span>activity_third<span class="token punctuation">,</span> container<span class="token punctuation">,</span> <span class="token boolean">false</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="4"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="5"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="6"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">13</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
</ol>
<h3 id="动态引入fragment"><a class="markdownIt-Anchor" href="#动态引入fragment">#</a> 动态引入 Fragment</h3>
<ul>
<li>
<p>创建待添加 Fragment 得到实例</p>
</li>
<li>
<p>获取 FragmentManager，在 Activity 中可以直接调用 <code>getSupportFragmentManager()</code>  方法获取</p>
</li>
<li>
<p>开启一个事务，通过调用 <code>beginTransaction()</code>  方法开启</p>
</li>
<li>
<p>向容器内添加或替换 Fragment，一般使用 <code>replace()</code>  方法实现，需要传入容器的 id 和待添加的 Fragment 实例</p>
</li>
<li>
<p>提交事务，调用 <code>commit()</code>  方法来完成</p>
</li>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">fun</span> <span class="token function">replaceFragment</span><span class="token punctuation">(</span>fragment<span class="token operator">:</span> TestFragment<span class="token punctuation">)</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre>        <span class="token keyword">val</span> fragmentManager <span class="token operator">=</span> supportFragmentManager</pre></td></tr><tr><td data-num="3"></td><td><pre></pre></td></tr><tr><td data-num="4"></td><td><pre>        <span class="token keyword">val</span> transaction <span class="token operator">=</span> fragmentManager<span class="token punctuation">.</span><span class="token function">beginTransaction</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="5"></td><td><pre></pre></td></tr><tr><td data-num="6"></td><td><pre>        transaction<span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span>R<span class="token punctuation">.</span>id<span class="token punctuation">.</span>testFragment<span class="token punctuation">,</span> fragment<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="7"></td><td><pre></pre></td></tr><tr><td data-num="8"></td><td><pre>        transaction<span class="token punctuation">.</span><span class="token function">commit</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="9"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="10"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">14</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
<h3 id="fragment和activity之间的交互"><a class="markdownIt-Anchor" href="#fragment和activity之间的交互">#</a> Fragment 和 Activity 之间的交互</h3>
<ul>
<li>
<p>Activity 调用 Fragment 方法，可以使用 <code>findViewById()</code>  或者视图绑定获取 Fragment 对象调用他的方法</p>
</li>
<li>
<p>Fragment 中使用 <code>getActivity()</code>  方法获取 Activity 对象调用他的方法</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">if</span> <span class="token punctuation">(</span>activity <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre>    <span class="token keyword">val</span> mainActivity <span class="token operator">=</span> activity <span class="token keyword">as</span> MainActivity</pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">15</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>需要注意的是</p>
<ul>
<li>动态注册的 <code>BroadcastReceiver</code>  一定要取消注册才行，在 <code>onDestroy()</code>  方法中通过调用 <code>unregisterReceiver()</code>  方法来实现</li>
</ul>
</li>
</ul>
<h4 id="静态注册实现开机启动"><a class="markdownIt-Anchor" href="#静态注册实现开机启动">#</a> 静态注册实现开机启动</h4>
<ul>
<li>
<p>动态注册的 <code>BroadcastReceiver</code>  可以自由的控制注册与注销，在灵活性有很多的有事，但是他存在着一个缺点，即必须在程序启动弄之后才能接收广播，因为注册的逻辑是写在 <code>onCreate()</code>  方法中的，而如果想要让程序在未启动的情况下也能接收到广播，就需要使用静态注册的方式</p>
</li>
<li>
<p>在 Android 8.0 系统之后，所有隐式广播都不允许使用静态注册的方式接收了，隐式广播值得是那些没有具体制定发送给哪个应用程序的广播，大多数系统广播属于隐式广播，但是少数特殊的系统广播仍然允许使用静态注册的方式来接收</p>
<ul>
<li>可以从这个网址查看这些特殊的系统广播
<ul>
<li><a class="link"   target="_blank" rel="noopener" href="https://developer.android.google.cn/guide/components/broadcast-exceptions.html" >https://developer.android.google.cn/guide/components/broadcast-exceptions.html <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a></li>
</ul>
</li>
</ul>
</li>
<li>
<div class="highlight-container" data-rel="Xml"><figure class="iseeu highlight xml"><figcaption data-lang="XML"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token prolog">&lt;?xml version="1.0" encoding="utf-8"?></span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>manifest</span> <span class="token attr-name"><span class="token namespace">xmlns:</span>android</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://schemas.android.com/apk/res/android<span class="token punctuation">"</span></span></pre></td></tr><tr><td data-num="3"></td><td><pre>          <span class="token attr-name"><span class="token namespace">xmlns:</span>tool</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://schemas.android.com/tools<span class="token punctuation">"</span></span></pre></td></tr><tr><td data-num="4"></td><td><pre>          <span class="token attr-name">package</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>cn.wenhe9.testmenu<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="5"></td><td><pre></pre></td></tr><tr><td data-num="6"></td><td><pre>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>uses-permission</span> <span class="token attr-name"><span class="token namespace">android:</span>name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>android.permission.RECEIVE_BOOT_COMPLETED<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span></pre></td></tr><tr><td data-num="7"></td><td><pre></pre></td></tr><tr><td data-num="8"></td><td><pre>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>application</span></pre></td></tr><tr><td data-num="9"></td><td><pre>            <span class="token attr-name"><span class="token namespace">android:</span>allowBackup</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span></pre></td></tr><tr><td data-num="10"></td><td><pre>            <span class="token attr-name"><span class="token namespace">android:</span>icon</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>@mipmap/ic_launcher<span class="token punctuation">"</span></span></pre></td></tr><tr><td data-num="11"></td><td><pre>            <span class="token attr-name"><span class="token namespace">android:</span>label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>@string/app_name<span class="token punctuation">"</span></span></pre></td></tr><tr><td data-num="12"></td><td><pre>            <span class="token attr-name"><span class="token namespace">android:</span>roundIcon</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>@mipmap/ic_launcher_round<span class="token punctuation">"</span></span></pre></td></tr><tr><td data-num="13"></td><td><pre>            <span class="token attr-name"><span class="token namespace">android:</span>supportsRtl</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span></pre></td></tr><tr><td data-num="14"></td><td><pre>            <span class="token attr-name"><span class="token namespace">android:</span>theme</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>@style/Theme.TestMenu<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="15"></td><td><pre>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>receiver</span></pre></td></tr><tr><td data-num="16"></td><td><pre>                <span class="token attr-name"><span class="token namespace">android:</span>name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>.BootCompleteReceiver<span class="token punctuation">"</span></span></pre></td></tr><tr><td data-num="17"></td><td><pre>                <span class="token attr-name"><span class="token namespace">android:</span>enabled</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span></pre></td></tr><tr><td data-num="18"></td><td><pre>                <span class="token attr-name"><span class="token namespace">android:</span>exported</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="19"></td><td><pre>            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>intent-filter</span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="20"></td><td><pre>                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>action</span> <span class="token attr-name"><span class="token namespace">android:</span>name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>android.intent.action.BOOT_COMPLETED<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span></pre></td></tr><tr><td data-num="21"></td><td><pre>            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>intent-filter</span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="22"></td><td><pre>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>receiver</span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="23"></td><td><pre>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>activity</span> <span class="token attr-name"><span class="token namespace">android:</span>name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>.MainActivity<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="24"></td><td><pre>            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>intent-filter</span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="25"></td><td><pre>                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>action</span> <span class="token attr-name"><span class="token namespace">android:</span>name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>android.intent.action.MAIN<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span></pre></td></tr><tr><td data-num="26"></td><td><pre></pre></td></tr><tr><td data-num="27"></td><td><pre>                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>category</span> <span class="token attr-name"><span class="token namespace">android:</span>name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>android.intent.category.LAUNCHER<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span></pre></td></tr><tr><td data-num="28"></td><td><pre>            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>intent-filter</span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="29"></td><td><pre>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>activity</span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="30"></td><td><pre>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>application</span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="31"></td><td><pre></pre></td></tr><tr><td data-num="32"></td><td><pre><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>manifest</span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="33"></td><td><pre><span class="token comment"><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">- 需要注意的是，如果程序需要进行一些对用户比较敏感的操作，必须在``AndroidManifest.xml`中进行权限声明，否则程序会直接崩溃</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">### 发送自定义广播</span><br><span class="line"></span><br><span class="line">#### 发送标准广播</span><br><span class="line"></span><br><span class="line">- ```kotlin</span><br><span class="line">  binding.testBroadcast.setOnClickListener &#123;</span><br><span class="line">      val intent = Intent(&quot;cn.wenhe9.testmenu.MY_Broadcast&quot;)</span><br><span class="line">      intent.setPackage(packageName)</span><br><span class="line">      sendBroadcast(intent)</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure></div></div></span></pre></td></tr></table></figure></li>
<li>
<p>需要注意的是，静态注册的 <code>BroadcastReceiver</code>  是无法接收隐式广播的，而默认情况下，我们发出的自定义广播恰恰都是隐式广播，因此这里一定要调用 <code>setPackage()</code>  方法，指定这条广播是发送给哪个应用程序的，从而让他变成一条显示广播，否则静态注册的 <code>BroadcastReceiver</code>  将无法接收到这条广播</p>
</li>
<li>
<p>当然，如果你的 <code>BroadcastReceiver</code>  是动态注册的，就不用了</p>
</li>
</ul>
<h4 id="发送有序广播"><a class="markdownIt-Anchor" href="#发送有序广播">#</a> 发送有序广播</h4>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre>binding<span class="token punctuation">.</span>testBroadcast<span class="token punctuation">.</span><span class="token function">setOnClickListener</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre>    <span class="token keyword">val</span> intent <span class="token operator">=</span> <span class="token function">Intent</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"cn.wenhe9.testmenu.MY_Broadcast"</span></span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="3"></td><td><pre>    intent<span class="token punctuation">.</span><span class="token function">setPackage</span><span class="token punctuation">(</span>packageName<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="4"></td><td><pre>    <span class="token function">sendOrderedBroadcast</span><span class="token punctuation">(</span>intent<span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="5"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="6"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">17</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr><tr><td data-num="7"></td><td><pre></pre></td></tr><tr><td data-num="8"></td><td><pre><span class="token operator">-</span> 动态注册</pre></td></tr><tr><td data-num="9"></td><td><pre></pre></td></tr><tr><td data-num="10"></td><td><pre>  <span class="token operator">-</span> ```kotlin</pre></td></tr><tr><td data-num="11"></td><td><pre>    <span class="token keyword">val</span> intentFilter <span class="token operator">=</span> <span class="token function">IntentFilter</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="12"></td><td><pre>    intentFilter<span class="token punctuation">.</span><span class="token function">addAction</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"android.intent.action.TIME_TICK"</span></span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="13"></td><td><pre>    intentFilter<span class="token punctuation">.</span>priority <span class="token operator">=</span> <span class="token number">100</span></pre></td></tr><tr><td data-num="14"></td><td><pre>    timeChangeReceiver <span class="token operator">=</span> <span class="token function">TimeChangeReceiver</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="15"></td><td><pre>    <span class="token function">registerReceiver</span><span class="token punctuation">(</span>timeChangeReceiver<span class="token punctuation">,</span> intentFilter<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="16"></td><td><pre>    <span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">18</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
<h2 id="持久化技术"><a class="markdownIt-Anchor" href="#持久化技术">#</a> 持久化技术</h2>
<h3 id="文件存储"><a class="markdownIt-Anchor" href="#文件存储">#</a> 文件存储</h3>
<ul>
<li>文件存储是 Android 中最近本的数据存储方式，他不对存储的内容进行任何格式化处理，所有数据都是原封不动的把偶才能到文件中，因而他比较适合存储一些简单的文本数据或二进制数据</li>
</ul>
<h4 id="将数据存储到文件中"><a class="markdownIt-Anchor" href="#将数据存储到文件中">#</a> 将数据存储到文件中</h4>
<ul>
<li>
<p>Context 类中提供了一个 <code>opneFileOutput()</code>  方法，可以用于将数据存储到指定的文件中，这个方法接收两个参数</p>
<ul>
<li>第一个参数是文件名，在文件创建的时候使用，注意这里指定的文件名不可以包含路径，因为所有的文件都默认存储在 <code>/data/data/&lt;package name&gt;/files</code>  目录下</li>
<li>第二个参数是文件的操作模式，主要有
<ul>
<li><code>MODE_PRIVATE</code>
<ul>
<li>默认是 <code>MODE_PRIVATE</code> ，表示当指定相同文件名的时候，所写入的内容会覆盖原文件中的内容</li>
</ul>
</li>
<li><code>MODE_APPEND</code>
<ul>
<li>表示如果该文件已存在，就往文件里面追加内容，不存在就创建新文件</li>
</ul>
</li>
<li>其实文件的操作模式本来还有另外两种， <code>MODE_WORLD_READABLE</code>  和 <code>MODE_WORLD_WRITEABLE</code> ，这两种模式表示允许其他应用程序对我们程序的文件进行读写操作，不过由于这两种模式过于危险，很容易引起应用的安全漏洞，在 Android 4.2 版本中被废弃</li>
</ul>
</li>
</ul>
</li>
<li>
<p><code>openFileOutput()</code>  方法返回的是一个 <code>FileOutputSteram</code>  对象，得到这个对象之后就可以使用 JAVA 流的方式将数据写入文件中了</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">fun</span> <span class="token function">save</span><span class="token punctuation">(</span>inputText <span class="token operator">:</span> String<span class="token punctuation">)</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre>    <span class="token keyword">try</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="3"></td><td><pre>        <span class="token keyword">val</span> output <span class="token operator">=</span> <span class="token function">openFileOutput</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"data"</span></span><span class="token punctuation">,</span> Context<span class="token punctuation">.</span>MODE_PRIVATE<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="4"></td><td><pre>		<span class="token keyword">val</span> writer <span class="token operator">=</span> <span class="token function">BufferedWriter</span><span class="token punctuation">(</span><span class="token function">OutputStream</span><span class="token punctuation">(</span>output<span class="token punctuation">)</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="5"></td><td><pre>        write<span class="token punctuation">.</span><span class="token function">use</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="6"></td><td><pre>            it<span class="token punctuation">.</span><span class="token function">write</span><span class="token punctuation">(</span>inputText<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="7"></td><td><pre>        <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="8"></td><td><pre>    <span class="token punctuation">&#125;</span><span class="token keyword">catch</span><span class="token punctuation">(</span>e <span class="token operator">:</span> IOException<span class="token punctuation">)</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="9"></td><td><pre>        e<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="10"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="11"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="12"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">19</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
</ul>
<h3 id="sharedpreferences存储"><a class="markdownIt-Anchor" href="#sharedpreferences存储">#</a> SharedPreferences 存储</h3>
<ul>
<li>不同于文件的存储方式，SharedPreferences 是使用键值对的方式来存储数据的。也就是说，当保存一条数据的时候，需要给这条数据提供一个对应的键，这样在读取数据的时候就可以通过这个键把相应的值取出来。而且 SharedPreferences 还支持多种不同的数据类型存储，如果存储的数据类型是整型，那么读取出来的数据也是整型的；如果存储的数据是一个字符串，那么读取出来的数据仍然是字符串。</li>
</ul>
<h4 id="获取sharedpreferences的两种方法"><a class="markdownIt-Anchor" href="#获取sharedpreferences的两种方法">#</a> 获取 SharedPreferences 的两种方法</h4>
<ul>
<li>
<p><strong>Context 类的 <code>getSharedPreferences()</code>  方法</strong></p>
<ul>
<li>此方法接收两个参数
<ul>
<li>第一个参数用于指定 SharedPreferences 文件的名称，如果指定的文件不存在则会创建一个，SharedPreferences 文件都是存放在 <code>/data/data/&lt;package name&gt;/shared_prefs/</code>  目录下的</li>
<li>第二个参数用于指定操作模式，目前只有默认的 <code>MODE_PRIVATE</code>  一种模式可选，他和直接传入 0 的效果是相同的，表示只有当前的应用程序才可以对这个 SharedPreferences 文件进行读写，其他集中操作模式均已被废弃， <code>MODE_WORLD_READABLE</code>  和 <code>MODE_WORLD_WRITEABLE</code>  这两种模式是在 Android 4.2 版本中被废弃的，MODE_MULTI_PROCESS 模式是在 Android 6.0 版本中被废弃的。</li>
</ul>
</li>
</ul>
</li>
<li>
<p><strong>Activity 类中的 <code>getPreferences()</code>  方法</strong></p>
<ul>
<li>这个方法和 Context 中的 <code>getSharedPreferences()</code>  方法很相似，不过它只接收一个操作模式参数，因为使用这个方法时会自动将当前 Activity 的类名作为 SharedPreferences 的文件名</li>
<li>得到了 SharedPreferences 对象之后，就可以开始向 SharedPreferences 文件中存储数据了，主要可以分为三步实现
<ol>
<li>调用 SharedPreferences 对象的 <code>edit()</code>  方法获取一个 <code>SharedPreferences.Editor</code>  对象</li>
<li>向 <code>SharedPreferences.Editor</code>  对象中添加数据，比如添加一个布尔型数据就是用 <code>putBoolean()</code>  方法，添加一个字符串则使用 <code>putString()</code>  方法，以此类推</li>
<li>调用 <code>apply()</code>  方法将添加的数据提交，从而完成数据存储操作</li>
</ol>
</li>
</ul>
</li>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre>binding<span class="token punctuation">.</span>addNum<span class="token punctuation">.</span><span class="token function">setOnClickListener</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre>    <span class="token keyword">val</span> editor <span class="token operator">=</span> <span class="token function">getSharedPreferences</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"data"</span></span><span class="token punctuation">,</span> Context<span class="token punctuation">.</span>MODE_PRIVATE<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">edit</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="3"></td><td><pre></pre></td></tr><tr><td data-num="4"></td><td><pre>    editor<span class="token punctuation">.</span><span class="token function">putString</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"key"</span></span><span class="token punctuation">,</span> <span class="token string-literal singleline"><span class="token string">"value"</span></span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="5"></td><td><pre>    editor<span class="token punctuation">.</span><span class="token function">putBoolean</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"true"</span></span><span class="token punctuation">,</span> <span class="token boolean">false</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="6"></td><td><pre>    editor<span class="token punctuation">.</span><span class="token function">putInt</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"1"</span></span><span class="token punctuation">,</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="7"></td><td><pre></pre></td></tr><tr><td data-num="8"></td><td><pre>    editor<span class="token punctuation">.</span><span class="token function">apply</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="9"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="10"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">20</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
<h3 id="sqlite数据库存储"><a class="markdownIt-Anchor" href="#sqlite数据库存储">#</a> SQLite 数据库存储</h3>
<h4 id="创建数据库"><a class="markdownIt-Anchor" href="#创建数据库">#</a> 创建数据库</h4>
<ul>
<li>
<p>android 为了让我们能够鞥家方便的管理数据库，专门提供了一个 <code>SQLiteOpenHelper</code>  帮助类，借助这个类可以非常简单的对数据库进行创建和升级</p>
</li>
<li>
<p>首先 <code>SQLiteOpenHelper</code>  是一个抽象类，所有需要创建一个自己的类去继承他， <code>SQLiteOpenHelper</code>  中有两个抽象方法: <code>onCreate()</code>  和 <code>onUpgrade()</code> ，我们必须在自己的帮助类里重写这两个方法，然后分别在这两个方法中实现创建和升级数据库的逻辑</p>
</li>
<li>
<p><code>SQLiteOpenHelper</code>  中还有两个非常重要的实例方法，这两个方法都可以创建或打开一个现有的数据库（如果数据库已存在则直接打开，否则要创建一个新的数据库）并返回一个可对数据库进行读写操作的对象</p>
<ul>
<li><code>gerReadableDatabase()</code></li>
<li><code>getWritableDatabase()</code></li>
<li>需要注意的是：
<ul>
<li>当数据库不可写入的时候（如磁盘空间已满）, <code>getReadableDatabase()</code>  方法返回的对象将以只读的方式打开数据库，而 <code>getWritableDatabase()</code>  方法则将出现异常</li>
</ul>
</li>
</ul>
</li>
<li>
<p><code>SQLiteOpebHelper</code>  中有两个构造方法可供重写，一般使用参数少点的那个构造方法即可，这个构造方法中接收 4 个参数</p>
<ul>
<li>第一个参数是 Context，这个没有好说的，必须有他才能对数据库进行操作</li>
<li>第二个是数据库名，创建数据库时使用的就是这里指定的名称</li>
<li>第三个参数允许我们在查询数据的时候返回一个自定的 Cursor，一般穿入 null 即可</li>
<li>第四个参数表示当前数据库的版本号，可用于对数据库进行升级操作</li>
</ul>
</li>
<li>
<p>构造出 SQLiteOpenHelper 的实例之后，再调用他的 <code>getReadableDatabase()</code>  或 <code>getWritabledatabase()</code>  方法就能够创建数据库了，数据库文件会存放在 <code>/data/data/&lt;package name&gt;/databases/</code>  目录下，此时，重写的 onCreate () 方法也会得到执行，所以通常会在这里处理一些创建表的逻辑</p>
</li>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">class</span> <span class="token function">MyDatabaseHelper</span><span class="token punctuation">(</span>context <span class="token operator">:</span> Context<span class="token punctuation">,</span> name <span class="token operator">:</span> String<span class="token punctuation">,</span> version <span class="token operator">:</span> Int<span class="token punctuation">)</span> <span class="token operator">:</span> <span class="token function">SQLiteOpenHelper</span><span class="token punctuation">(</span>context<span class="token punctuation">,</span> name<span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">,</span> version<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre></pre></td></tr><tr><td data-num="3"></td><td><pre>    <span class="token keyword">private</span> <span class="token keyword">val</span> createBook <span class="token operator">=</span> <span class="token string-literal singleline"><span class="token string">"create table Book(\n"</span></span> <span class="token operator">+</span></pre></td></tr><tr><td data-num="4"></td><td><pre>            <span class="token string-literal singleline"><span class="token string">"\tid int primary key autoincrement,\n"</span></span> <span class="token operator">+</span></pre></td></tr><tr><td data-num="5"></td><td><pre>            <span class="token string-literal singleline"><span class="token string">"\tusername varchar(20),\n"</span></span> <span class="token operator">+</span></pre></td></tr><tr><td data-num="6"></td><td><pre>            <span class="token string-literal singleline"><span class="token string">"\tpassword varchar(20)\n"</span></span> <span class="token operator">+</span></pre></td></tr><tr><td data-num="7"></td><td><pre>            <span class="token string-literal singleline"><span class="token string">");"</span></span></pre></td></tr><tr><td data-num="8"></td><td><pre></pre></td></tr><tr><td data-num="9"></td><td><pre>    <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">onCreate</span><span class="token punctuation">(</span>db<span class="token operator">:</span> SQLiteDatabase<span class="token operator">?</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="10"></td><td><pre>        db<span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">execSQL</span><span class="token punctuation">(</span>createBook<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="11"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="12"></td><td><pre></pre></td></tr><tr><td data-num="13"></td><td><pre>    <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">onUpgrade</span><span class="token punctuation">(</span>db<span class="token operator">:</span> SQLiteDatabase<span class="token operator">?</span><span class="token punctuation">,</span> oldVersion<span class="token operator">:</span> Int<span class="token punctuation">,</span> newVersion<span class="token operator">:</span> Int<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="14"></td><td><pre>        <span class="token keyword">if</span> <span class="token punctuation">(</span>oldVersion <span class="token operator">&lt;=</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="15"></td><td><pre>            db<span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">execSQL</span><span class="token punctuation">(</span>createBook<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="16"></td><td><pre>        <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="17"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="18"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="19"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">21</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
<h4 id="升级数据库"><a class="markdownIt-Anchor" href="#升级数据库">#</a> 升级数据库</h4>
<ul>
<li>在 <code>onUpgrade()</code>  写升级的逻辑，创建数据库时提供更高的版本号</li>
</ul>
<h4 id="添加数据"><a class="markdownIt-Anchor" href="#添加数据">#</a> 添加数据</h4>
<ul>
<li>
<p>调用 <code>SQLiteOpenHelper</code>  的 <code>getReadableDatabase()</code>  和 <code>getWriteableDatabase()</code>  方法除了可以用于创建和升级数据库，他们还都会返回一个 <code>SQLiteDatabase</code>  对象，借助这个对象可以对数据进行 CRUD 操作</p>
</li>
<li>
<p><code>SQLiteDatabase</code>  中提供了一个 <code>insert()</code>  方法，专门用于添加数据，他接收三个参数</p>
<ul>
<li>第一个参数是表名，我们希望向哪张表添加数据，这里就传入该表的名字</li>
<li>第二个参数用于在未指定添加数据的情况下个某系可为空的列自动赋值 NULL，一般我们用不到这个功能，直接传入 null 即可</li>
<li>第三个参数是一个 <code>ContentValues</code>  对象，他提供了一些列的 <code>put()</code>  方法重载，用于向 <code>ContentValues</code>  中添加数据，只需要将表中的每个列名以及相应的待添加数据传入即可</li>
</ul>
</li>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre>binding<span class="token punctuation">.</span>addNum<span class="token punctuation">.</span><span class="token function">setOnClickListener</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre>    <span class="token keyword">val</span> db <span class="token operator">=</span> dbHelper<span class="token punctuation">.</span>writableDatabase</pre></td></tr><tr><td data-num="3"></td><td><pre>    <span class="token keyword">val</span> values <span class="token operator">=</span> <span class="token function">ContentValues</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">apply</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="4"></td><td><pre>        <span class="token function">put</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"username"</span></span><span class="token punctuation">,</span> <span class="token string-literal singleline"><span class="token string">"沙扬娜拉"</span></span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="5"></td><td><pre>        <span class="token function">put</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"password"</span></span><span class="token punctuation">,</span> <span class="token string-literal singleline"><span class="token string">"123456"</span></span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="6"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="7"></td><td><pre>    <span class="token comment">//val values = contentValuesOf ("username" to "沙扬娜拉", "passsword" to "1234456")</span></pre></td></tr><tr><td data-num="8"></td><td><pre>    db<span class="token punctuation">.</span><span class="token function">insert</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"book"</span></span><span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">,</span> values<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="9"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="10"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">22</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
<h4 id="删除数据"><a class="markdownIt-Anchor" href="#删除数据">#</a> 删除数据</h4>
<ul>
<li>
<p><code>SQLiteDatabase</code>  中提供了一个 <code>delete()</code>  方法，专门用于删除数据，这个方法接收三个参数：</p>
<ul>
<li>第一个参数仍然是表名</li>
<li>第二、三个参数用于约束删除某一行或某几行的数据，不指定的话默认删除所有行</li>
</ul>
</li>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre>binding<span class="token punctuation">.</span>addNum<span class="token punctuation">.</span><span class="token function">setOnClickListener</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre>    <span class="token keyword">val</span> db <span class="token operator">=</span> dbHelper<span class="token punctuation">.</span>writableDatabase</pre></td></tr><tr><td data-num="3"></td><td><pre>    db<span class="token punctuation">.</span><span class="token function">delete</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"book"</span></span><span class="token punctuation">,</span> <span class="token string-literal singleline"><span class="token string">"pages > ?"</span></span><span class="token punctuation">,</span> <span class="token function">arrayOf</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"500"</span></span><span class="token punctuation">)</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="5"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">23</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
<li>
<p>我们首先在查询按钮的点击事件里面调用了 SQLiteDatabase 的 query () 方法查询数据。这里的 query () 方法非常简单，只使用了第一个参数指明查询 Book 表，后面的参数全部为 null。这就表示希望查询这张表中的所有数据，虽然这张表中目前只剩下一条数据了。查询完之后就得到了一个 Cursor 对象，接着我们调用它的 moveToFirst () 方法，将数据的指针移动到第一行的位置，然后进入一个循环当中，去遍历查询到的每一行数据。在这个循环中可以通过 Cursor 的 getColumnIndex () 方法获取某一列在表中对应的位置索引，然后将这个索引传入相应的取值方法中，就可以得到从数据库中读取到的数据了。接着我们使用 Log 将取出的数据打印出来，借此检查读取工作有没有成功完成。最后别忘了调用 close () 方法来关闭 Cursor。</p>
</li>
</ul>
<h4 id="使用sql操作数据库"><a class="markdownIt-Anchor" href="#使用sql操作数据库">#</a> 使用 SQL 操作数据库</h4>
<ul>
<li>
<p>添加数据</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre>db<span class="token punctuation">.</span><span class="token function">execSQL</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"insert into Book (name, author, pages, price) values(?, ?, ?, ?)"</span></span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="2"></td><td><pre>    <span class="token function">arrayOf</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"The Da Vinci Code"</span></span><span class="token punctuation">,</span> <span class="token string-literal singleline"><span class="token string">"Dan Brown"</span></span><span class="token punctuation">,</span> <span class="token string-literal singleline"><span class="token string">"454"</span></span><span class="token punctuation">,</span> <span class="token string-literal singleline"><span class="token string">"16.96"</span></span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="4"></td><td><pre>db<span class="token punctuation">.</span><span class="token function">execSQL</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"insert into Book (name, author, pages, price) values(?, ?, ?, ?)"</span></span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="5"></td><td><pre>    <span class="token function">arrayOf</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"The Lost Symbol"</span></span><span class="token punctuation">,</span> <span class="token string-literal singleline"><span class="token string">"Dan Brown"</span></span><span class="token punctuation">,</span> <span class="token string-literal singleline"><span class="token string">"510"</span></span><span class="token punctuation">,</span> <span class="token string-literal singleline"><span class="token string">"19.95"</span></span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="6"></td><td><pre><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="7"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">24</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>删除数据</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre>db<span class="token punctuation">.</span><span class="token function">execSQL</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"delete from Book where pages > ?"</span></span><span class="token punctuation">,</span> <span class="token function">arrayOf</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"500"</span></span><span class="token punctuation">)</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">25</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
</ul>
<h2 id="运行时权限"><a class="markdownIt-Anchor" href="#运行时权限">#</a> 运行时权限</h2>
<h3 id="android权限机制详解"><a class="markdownIt-Anchor" href="#android权限机制详解">#</a> Android 权限机制详解</h3>
<ul>
<li>
<p>首先回顾一下过去 Android 的权限机制。我们在第 6 章写 BroadcastTest 项目的时候第一次接触了 Android 权限相关的内容，当时为了要监听开机广播，我们在 AndroidManifest.xml 文件中添加了这样一句权限声明：</p>
<ul>
<li>
<div class="highlight-container" data-rel="Xml"><figure class="iseeu highlight xml"><figcaption data-lang="XML"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>manifest</span> <span class="token attr-name"><span class="token namespace">xmlns:</span>android</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://schemas.android.com/apk/res/android<span class="token punctuation">"</span></span></pre></td></tr><tr><td data-num="2"></td><td><pre>    <span class="token attr-name">package</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>com.example.broadcasttest<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="3"></td><td><pre></pre></td></tr><tr><td data-num="4"></td><td><pre>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>uses-permission</span> <span class="token attr-name"><span class="token namespace">android:</span>name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>android.permission.RECEIVE_BOOT_COMPLETED<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></pre></td></tr><tr><td data-num="5"></td><td><pre>    ...</pre></td></tr><tr><td data-num="6"></td><td><pre><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>manifest</span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="7"></td><td><pre><span class="token comment"><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">  - 因为监听开机广播涉及了用户设备的安全，因此必须在AndroidManifest.xml中加入权限声明，否则我们的程序就会崩溃。</span><br><span class="line"></span><br><span class="line">- 那么现在问题来了，加入了这句权限声明后，对于用户来说到底有什么影响呢？为什么这样就可以保护用户设备的安全了呢？</span><br><span class="line"></span><br><span class="line">- 其实用户主要在两个方面得到了保护。一方面，如果用户在低于Android 6.0系统的设备上安装该程序，会在安装界面给出如图8.1所示的提醒。这样用户就可以清楚地知晓该程序一共申请了哪些权限，从而决定是否要安装这个程序。</span><br><span class="line"></span><br><span class="line">  - ![image-20210925162855446](http://tuchuang.wenhe9.cn/image-20210925162855446.png)</span><br><span class="line"></span><br><span class="line">- 另一方面，用户可以随时在应用程序管理界面查看任意一个程序的权限申请情况，如图8.2所示。这样该程序申请的所有权限就尽收眼底，什么都瞒不过用户的眼睛，以此保证应用程序不会出现各种滥用权限的情况。</span><br><span class="line">  - ![image-20210925162918467](http://tuchuang.wenhe9.cn/image-20210925162918467.png)</span><br><span class="line">- 这种权限机制的设计思路其实非常简单，就是用户如果认可你所申请的权限，就会安装你的程序，如果不认可你所申请的权限，那么拒绝安装就可以了。</span><br><span class="line">- 但是理想是美好的，现实却很残酷。很多我们离不开的常用软件普遍存在着滥用权限的情况，不管到底用不用得到，反正先把权限申请了再说。比如微信所申请的权限列表如图8.3所示。</span><br><span class="line">  - ![image-20210925162959295](http://tuchuang.wenhe9.cn/image-20210925162959295.png)</span><br><span class="line">- 这还只是微信所申请的一半左右的权限，因为权限太多，一屏截不全。其中有一些权限我并不认可，比如微信为什么要读取我手机的短信和彩信？但是不认可又能怎样，难道我拒绝安装微信？没错，这种例子比比皆是，一些软件在让用户产生依赖以后就会容易 “店大欺客”，反正这个权限我就是要了，你自己看着办吧！</span><br><span class="line">- Android开发团队当然也意识到了这个问题，于是在Android 6.0系统中加入了运行时权限功能。也就是说，用户不需要在安装软件的时候一次性授权所有申请的权限，而是可以在软件的使用过程中再对某一项权限申请进行授权。比如一款相机应用在运行时申请了地理位置定位权限，就算我拒绝了这个权限，也应该可以使用这个应用的其他功能，而不是像之前那样直接无法安装它。</span><br><span class="line">- 当然，并不是所有权限都需要在运行时申请，对于用户来说，不停地授权也很烦琐。Android现在将常用的权限大致归成了两类，一类是普通权限，一类是危险权限。准确地讲，其实还有一些特殊权限，不过这些权限使用得相对较少，因此不在本书的讨论范围之内。普通权限指的是那些不会直接威胁到用户的安全和隐私的权限，对于这部分权限申请，系统会自动帮我们进行授权，不需要用户手动操作，比如在BroadcastTest项目中申请的权限就是普通权限。危险权限则表示那些可能会触及用户隐私或者对设备安全性造成影响的权限，如获取设备联系人信息、定位设备的地理位置等，对于这部分权限申请，必须由用户手动授权才可以，否则程序就无法使用相应的功能。</span><br><span class="line">- 但是Android中一共有上百种权限，我们怎么从中区分哪些是普通权限，哪些是危险权限呢？其实并没有那么难，因为危险权限总共就那么些，除了危险权限之外，剩下的大多就是普通权限了。表8.1列出了到Android 10系统为止所有的危险权限，一共是11组30个权限。</span><br><span class="line">  - ![img](http://tuchuang.wenhe9.cn/epub_37683759_237)</span><br><span class="line">- 这张表格你看起来可能并不会那么轻松，因为里面的权限全都是你没使用过的。不过没有关系，你并不需要了解表格中每个权限的作用，只要把它当成一个参照表来查看就行了。每当要使用一个权限时，可以先到这张表中查一下，如果是这张表中的权限，就需要进行运行时权限处理，否则，只需要在AndroidManifest.xml文件中添加一下权限声明就可以了。</span><br><span class="line">- 另外注意，表格中每个危险权限都属于一个权限组，我们在进行运行时权限处理时使用的是权限名。原则上，用户一旦同意了某个权限申请之后，同组的其他权限也会被系统自动授权。但是请谨记，不要基于此规则来实现任何功能逻辑，因为Android系统随时有可能调整权限的分组。</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">### 在程序运行时申请权限</span><br><span class="line"></span><br><span class="line">- ```kotlin</span><br><span class="line">  package cn.wenhe9.testmenu</span><br><span class="line">  </span><br><span class="line">  import android.Manifest</span><br><span class="line">  import android.content.Intent</span><br><span class="line">  import android.content.pm.PackageManager</span><br><span class="line">  import android.net.Uri</span><br><span class="line">  import androidx.appcompat.app.AppCompatActivity</span><br><span class="line">  import android.os.Bundle</span><br><span class="line">  import android.widget.Toast</span><br><span class="line">  import androidx.core.app.ActivityCompat</span><br><span class="line">  import androidx.core.content.ContextCompat</span><br><span class="line">  import cn.wenhe9.testmenu.databinding.ActivityForthBinding</span><br><span class="line">  import cn.wenhe9.testmenu.databinding.ActivityMainBinding</span><br><span class="line">  </span><br><span class="line">  class ForthActivity : AppCompatActivity() &#123;</span><br><span class="line">  </span><br><span class="line">      private lateinit var binding : ActivityForthBinding</span><br><span class="line">  </span><br><span class="line">      override fun onCreate(savedInstanceState: Bundle?) &#123;</span><br><span class="line">          super.onCreate(savedInstanceState)</span><br><span class="line">          binding = ActivityForthBinding.inflate(layoutInflater)</span><br><span class="line">          setContentView(binding.root)</span><br><span class="line">  </span><br><span class="line">          binding.callPhone.setOnClickListener &#123;</span><br><span class="line">              if (ContextCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED)&#123;</span><br><span class="line">                  ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CALL_PHONE), 1)</span><br><span class="line">              &#125;else &#123;</span><br><span class="line">                  call()</span><br><span class="line">              &#125;</span><br><span class="line">          &#125;</span><br><span class="line">      &#125;</span><br><span class="line">  </span><br><span class="line">      override fun onRequestPermissionsResult(requestCode: Int, permissions: Array&lt;out String&gt;, grantResults: IntArray) &#123;</span><br><span class="line">          super.onRequestPermissionsResult(requestCode, permissions, grantResults)</span><br><span class="line">          when (requestCode)&#123;</span><br><span class="line">              1 -&gt; &#123;</span><br><span class="line">                  if (grantResults.isNotEmpty() &amp;&amp; grantResults[0] == PackageManager.PERMISSION_GRANTED)&#123;</span><br><span class="line">                      call()</span><br><span class="line">                  &#125; else &#123;</span><br><span class="line">                      Toast.makeText(this, &quot;you denied the permission&quot;, Toast.LENGTH_SHORT).show()</span><br><span class="line">                  &#125;</span><br><span class="line">              &#125;</span><br><span class="line">          &#125;</span><br><span class="line">      &#125;</span><br><span class="line">  </span><br><span class="line">      fun call()&#123;</span><br><span class="line">          val intent = Intent(Intent.ACTION_VIEW)</span><br><span class="line">          intent.data = Uri.parse(&quot;tel:10086&quot;)</span><br><span class="line">          startActivity(intent)</span><br><span class="line">      &#125;</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure></div></div></span></pre></td></tr></table></figure></li>
</ul>
</li>
<li>
<p>上面的代码覆盖了运行时权限的完整流程，下面我们具体解析一下。说白了，运行时权限的核心就是在程序运行过程中由用户授权我们去执行某些危险操作，程序是不可以擅自做主去执行这些危险操作的。因此，第一步就是要先判断用户是不是已经给过我们授权了，借助的是 <code>ContextCompat.checkSelfPermission()</code>  方法。 <code>checkSelfPermission()</code>  方法接收两个参数：第一个参数是 Context，这个没什么好说的；第二个参数是具体的权限名，比如打电话的权限名就是 <code>Manifest.permission.CALL_PHONE</code> 。然后我们使用方法的返回值和 <code>PackageManager.PERMISSION_GRANTED</code>  做比较，相等就说明用户已经授权，不等就表示用户没有授权。</p>
</li>
<li>
<p>如果已经授权的话就简单了，直接执行拨打电话的逻辑操作就可以了，这里我们把拨打电话的逻辑封装到了 call () 方法当中。如果没有授权的话，则需要调用 <code>ActivityCompat.requestPermissions()</code>  方法向用户申请授权。 <code>requestPermissions()</code>  方法接收 3 个参数：第一个参数要求是 Activity 的实例；第二个参数是一个 String 数组，我们把要申请的权限名放在数组中即可；第三个参数是请求码，只要是唯一值就可以了，这里传入了 1。</p>
</li>
<li>
<p>调用完 <code>requestPermissions()</code>  方法之后，系统会弹出一个权限申请的对话框，用户可以选择同意或拒绝我们的权限申请。不论是哪种结果，最终都会回调到 <code>onRequestPermissionsResult()</code>  方法中，而授权的结果则会封装在 <code>grantResults</code>  参数当中。这里我们只需要判断一下最后的授权结果：如果用户同意的话，就调用 call () 方法拨打电话；如果用户拒绝的话，我们只能放弃操作，并且弹出一条失败提示。</p>
</li>
</ul>
<h2 id="contentprovider"><a class="markdownIt-Anchor" href="#contentprovider">#</a> ContentProvider</h2>
<ul>
<li>主要用于在不同的应用程序之间实现数据共享的功能，它提供可一套完整的机制，允许一个程序访问另一个程序中的数据，同时还能保证被访问数据的安全性。目前使用 <code>ContentProvier</code>  是 Android 实跨程序共享数据的标准方式</li>
<li>不同于文件存储和 SharedPreferences 存储中的两种全局可读写啊哦做模式，ContentProvider 可以选择只对哪一部分数据进行共享，从而保证我们程序中的隐私数据不会有泄露的风险</li>
</ul>
<h3 id="访问其他程序中的数据"><a class="markdownIt-Anchor" href="#访问其他程序中的数据">#</a> 访问其他程序中的数据</h3>
<ul>
<li>ContentProvider 的用法一般有两种：
<ul>
<li>使用现有的 ContentProvider 读取和操作相应程序中的数据</li>
<li>另一种是创建自己的 ContentProvider，给程序的数据提供外部访问接口</li>
</ul>
</li>
</ul>
<h3 id="contentresolver的基本用法"><a class="markdownIt-Anchor" href="#contentresolver的基本用法">#</a> ContentResolver 的基本用法</h3>
<ul>
<li>
<p>对于每一个应用程序来说，如果想要访问 ContentProvider 中共享的数据，就一定要借助 <code>ContentResolver</code>  类，可以通过 Context 中的 <code>getContentResolver()</code>  方法获取该类的实例， <code>ContentResolver</code>  中提供了一系列的方法用于对数据进行增删改查操作，其中 <code>insert()</code>  方法用于添加数据， <code>update()</code>  方法用于更新数据， <code>delete()</code>  方法用于删除数据， <code>query()</code>  用于查询数据</p>
</li>
<li>
<p>不同于 SQLiteDatabase，ContentResolver 中的增删改查方法都是不接收表名参数的，而是使用一个 Uri 参数代替，这个参数被称为内容 URI。内容 URI 给 ContentProvider 中的数据建立了唯一标识符，它主要由两部分组成： <code>authority</code>  和 <code>path</code> 。 <code>authority</code>  是用于对不同的应用程序做区分的，一般为了避免冲突，会采用应用包名的方式进行命名。比如某个应用的包名是 <code>com.example.app</code> ，那么该应用对应的 <code>authority</code>  就可以命名为 <code>com.example.app.provider</code> 。 <code>path</code>  则是用于对同一应用程序中不同的表做区分的，通常会添加到 <code>authority</code>  的后面。比如某个应用的数据库里存在两张表 table1 和 table2，这时就可以将 <code>path</code>  分别命名为 /table1 和 /table2，然后把 <code>authority</code>  和 <code>path</code>  进行组合，内容 URI 就变成了 <code>com.example.app.provider/table1</code>  和 <code>com.example.app.provider/table2</code> 。不过，目前还很难辨认出这两个字符串就是两个内容 URI，我们还需要在字符串的头部加上协议声明，因此，内容 URI 最标准的格式如下：</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre>content<span class="token operator">:</span><span class="token operator">/</span><span class="token operator">/</span>com<span class="token punctuation">.</span>example<span class="token punctuation">.</span>app<span class="token punctuation">.</span>provider<span class="token operator">/</span>table1</pre></td></tr><tr><td data-num="2"></td><td><pre>content<span class="token operator">:</span><span class="token operator">/</span><span class="token operator">/</span>com<span class="token punctuation">.</span>example<span class="token punctuation">.</span>app<span class="token punctuation">.</span>provider<span class="token operator">/</span>table2</pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">27</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
<li>
<p>只需要调用 <code>Uri.parse()</code>  方法，就可以将内容 URI 字符串解析成 Uri 对象了</p>
</li>
</ul>
</li>
<li>
<p>然后既可以使用这个 Uri 对象查询 table1 表中的数据了</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">val</span> cursor <span class="token operator">=</span> contentResolver<span class="token punctuation">.</span><span class="token function">query</span><span class="token punctuation">(</span></pre></td></tr><tr><td data-num="2"></td><td><pre>	uri<span class="token punctuation">,</span></pre></td></tr><tr><td data-num="3"></td><td><pre>    projection<span class="token punctuation">,</span></pre></td></tr><tr><td data-num="4"></td><td><pre>    selection<span class="token punctuation">,</span></pre></td></tr><tr><td data-num="5"></td><td><pre>    selectionArgs<span class="token punctuation">,</span></pre></td></tr><tr><td data-num="6"></td><td><pre>    sortOrder</pre></td></tr><tr><td data-num="7"></td><td><pre><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="8"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">28</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>添加数据</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">val</span> values <span class="token operator">=</span> <span class="token function">contentValuesof</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"column1"</span></span> <span class="token keyword">to</span> <span class="token string-literal singleline"><span class="token string">"text"</span></span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="2"></td><td><pre>contentResolver<span class="token punctuation">.</span><span class="token function">insert</span><span class="token punctuation">(</span>uri<span class="token punctuation">,</span> values<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">29</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>删除数据</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre>contentResolver<span class="token punctuation">.</span><span class="token function">delete</span><span class="token punctuation">(</span>uri<span class="token punctuation">,</span> <span class="token string-literal singleline"><span class="token string">"id = ?"</span></span><span class="token punctuation">,</span> <span class="token function">arrayOf</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"1"</span></span><span class="token punctuation">)</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">30</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
<li>
<p><code>onCreate()</code> ，初始化 ContentProvider 的时候调用，通常会在这里完成对数据库的创建和升级等操作，返回 true 表示 ContentProvider 初始化成功，返回 false 则表示失败</p>
</li>
<li>
<p><code>query()</code> ，从 ContentProvider 中查询数据</p>
<ul>
<li><code>uri</code>  参数用于确定查询哪张表，</li>
<li><code>projection</code>  参数用于确定查询哪些列</li>
<li><code>selection</code>  和 <code>selectionArgs</code>  参数用于约定查询哪些行</li>
<li><code>sortOrder</code>  参数用于对结果进行排序</li>
<li>查询的结果存放在 Cursor 对象中返回</li>
</ul>
</li>
<li>
<p><code>insert()</code></p>
<ul>
<li>向 ContentProvider 中添加一条数据，uri 参数用于确定要添加的表，待添加的数据保存在 values 参数中，待添加完成后，返回一个用于表示这条新纪录的 URI</li>
</ul>
</li>
<li>
<p><code>update()</code></p>
<ul>
<li>更新 ContentProvider 中已有的数据，uri 参数用于确定更新哪一张表中的数据，新数据保存在 values 参数中，selelction 和 selectionArgs 参数用于约束更新哪些行，受影响的行数将作为返回值返回</li>
</ul>
</li>
<li>
<p><code>delete()</code></p>
<ul>
<li>从 ContentProvider 中删除数据，uri 参数用于确定删除哪一行中的数据，selection 和 selectionArgs 参数用于约束删除哪些行，被删除的行数将作为返回值返回</li>
</ul>
</li>
<li>
<p><code>getType()</code></p>
<ul>
<li>根据传入的内容 URI 返回相应的 MINE 类型</li>
</ul>
</li>
</ul>
</li>
<li>
<p>一个标准的 URI 写法是</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre>content<span class="token operator">:</span><span class="token operator">/</span><span class="token operator">/</span>com<span class="token punctuation">.</span>example<span class="token punctuation">.</span>app<span class="token punctuation">.</span>provider<span class="token operator">/</span>table1</pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">31</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
<li>
<p>这就表示调用方期望访问的是 <code>com.example.app</code>  这个应用的 table1 表中 id 为 1 的数据</p>
</li>
</ul>
</li>
<li>
<p>内容 URI 的格式主要就只有以上两种</p>
<ul>
<li>以路径结尾表示期望访问该表中的所有数据</li>
<li>以 id 结尾表示期望访问该表中拥有相应 id 的数据</li>
</ul>
</li>
<li>
<p>可以使用通配符分别匹配这两种格式的内容 URI，规则如下：</p>
<ul>
<li><code>*</code>  表示匹配任意长度的字符串</li>
<li><code>#</code> 表示匹配任意长度的数字</li>
</ul>
</li>
<li>
<p>所以，一个能够匹配人意表的内容的 URI 格式就可以写成</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre>content<span class="token operator">:</span><span class="token operator">/</span><span class="token operator">/</span>com<span class="token punctuation">.</span>example<span class="token punctuation">.</span>app<span class="token punctuation">.</span>provider<span class="token comment">/*</pre></td></tr><tr><td data-num="2"></td><td><pre><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">- 一个能够匹配table1表中任意一行数据的内容URI格式就可以写成：</span><br><span class="line"></span><br><span class="line">  - ```kotlin</span><br><span class="line">    content://com.example.app.provider/table1/#</span><br></pre></td></tr></table></figure></div></div></span></pre></td></tr></table></figure></li>
</ul>
</li>
<li>
<p>接着，我们再借助 <code>UriMatcher</code>  这个类就可以轻松地实现匹配内容 URI 的功能</p>
</li>
<li>
<p><code>UriMathcer</code>  中提供了一个 <code>addUri()</code>  方法，这个方法接收 3 个参数，可以分别把 <code>authority</code> 、 <code>path</code> 、和一个自定义代码传进去。这样当调用 <code>UriMatcher</code>  的 <code>match()</code>  方法的时候，就可以将一个 Uri 对象传入，返回值是某一个能够匹配这个 Uri 对象所对应的自定义代码，利用这个代码，我们就可以判断出调用方期望访问的是哪张表中的数据了</p>
</li>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">class</span> MyProvider <span class="token operator">:</span> <span class="token function">ContentProvider</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre></pre></td></tr><tr><td data-num="3"></td><td><pre>    <span class="token keyword">private</span> <span class="token keyword">val</span> table1Dir <span class="token operator">=</span> <span class="token number">0</span></pre></td></tr><tr><td data-num="4"></td><td><pre>    <span class="token keyword">private</span> <span class="token keyword">val</span> table1Item <span class="token operator">=</span> <span class="token number">1</span></pre></td></tr><tr><td data-num="5"></td><td><pre>    <span class="token keyword">private</span> <span class="token keyword">val</span> table2Dir <span class="token operator">=</span> <span class="token number">2</span></pre></td></tr><tr><td data-num="6"></td><td><pre>    <span class="token keyword">private</span> <span class="token keyword">val</span> table2Item <span class="token operator">=</span> <span class="token number">3</span></pre></td></tr><tr><td data-num="7"></td><td><pre></pre></td></tr><tr><td data-num="8"></td><td><pre>    <span class="token keyword">private</span> <span class="token keyword">val</span> uriMatcher <span class="token operator">=</span> <span class="token function">UriMatcher</span><span class="token punctuation">(</span>UriMatcher<span class="token punctuation">.</span>NO_MATCH<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="9"></td><td><pre></pre></td></tr><tr><td data-num="10"></td><td><pre>    <span class="token keyword">init</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="11"></td><td><pre>        uriMatcher<span class="token punctuation">.</span><span class="token function">addURI</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"com.example.app.provider"</span></span><span class="token punctuation">,</span> <span class="token string-literal singleline"><span class="token string">"table1"</span></span><span class="token punctuation">,</span> table1Dir<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="12"></td><td><pre>        uriMatcher<span class="token punctuation">.</span><span class="token function">addURI</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"com.example.app.provider "</span></span><span class="token punctuation">,</span> <span class="token string-literal singleline"><span class="token string">"table1/#"</span></span><span class="token punctuation">,</span> table1Item<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="13"></td><td><pre>        uriMatcher<span class="token punctuation">.</span><span class="token function">addURI</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"com.example.app.provider "</span></span><span class="token punctuation">,</span> <span class="token string-literal singleline"><span class="token string">"table2"</span></span><span class="token punctuation">,</span> table2Dir<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="14"></td><td><pre>        uriMatcher<span class="token punctuation">.</span><span class="token function">addURI</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"com.example.app.provider "</span></span><span class="token punctuation">,</span> <span class="token string-literal singleline"><span class="token string">"table2/#"</span></span><span class="token punctuation">,</span> table2Item<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="15"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="16"></td><td><pre>    <span class="token operator">..</span><span class="token punctuation">.</span></pre></td></tr><tr><td data-num="17"></td><td><pre>    <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">query</span><span class="token punctuation">(</span>uri<span class="token operator">:</span> Uri<span class="token punctuation">,</span> projection<span class="token operator">:</span> Array<span class="token operator">&lt;</span>String<span class="token operator">></span><span class="token operator">?</span><span class="token punctuation">,</span> selection<span class="token operator">:</span> String<span class="token operator">?</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="18"></td><td><pre>            selectionArgs<span class="token operator">:</span> Array<span class="token operator">&lt;</span>String<span class="token operator">></span><span class="token operator">?</span><span class="token punctuation">,</span> sortOrder<span class="token operator">:</span> String<span class="token operator">?</span><span class="token punctuation">)</span><span class="token operator">:</span> Cursor<span class="token operator">?</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="19"></td><td><pre>        <span class="token keyword">when</span> <span class="token punctuation">(</span>uriMatcher<span class="token punctuation">.</span><span class="token function">match</span><span class="token punctuation">(</span>uri<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="20"></td><td><pre>            table1Dir <span class="token operator">-></span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="21"></td><td><pre>                <span class="token comment">// 查询 table1 表中的所有数据</span></pre></td></tr><tr><td data-num="22"></td><td><pre>            <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="23"></td><td><pre>            table1Item <span class="token operator">-></span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="24"></td><td><pre>                <span class="token comment">// 查询 table1 表中的单条数据</span></pre></td></tr><tr><td data-num="25"></td><td><pre>            <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="26"></td><td><pre>            table2Dir <span class="token operator">-></span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="27"></td><td><pre>                <span class="token comment">// 查询 table2 表中的所有数据</span></pre></td></tr><tr><td data-num="28"></td><td><pre>            <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="29"></td><td><pre>            table2Item <span class="token operator">-></span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="30"></td><td><pre>                <span class="token comment">// 查询 table2 表中的单条数据</span></pre></td></tr><tr><td data-num="31"></td><td><pre>            <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="32"></td><td><pre>        <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="33"></td><td><pre>        <span class="token operator">..</span><span class="token punctuation">.</span></pre></td></tr><tr><td data-num="34"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="35"></td><td><pre>    <span class="token operator">..</span><span class="token punctuation">.</span></pre></td></tr><tr><td data-num="36"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="37"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">33</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
<li>
<p>所以 <code>getType()</code>  方法就可以这么写：</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">class</span> MyProvider <span class="token operator">:</span> <span class="token function">ContentProvider</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre>    <span class="token operator">..</span><span class="token punctuation">.</span></pre></td></tr><tr><td data-num="3"></td><td><pre>    <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">getType</span><span class="token punctuation">(</span>uri<span class="token operator">:</span> Uri<span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token keyword">when</span> <span class="token punctuation">(</span>uriMatcher<span class="token punctuation">.</span><span class="token function">match</span><span class="token punctuation">(</span>uri<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="4"></td><td><pre>        table1Dir <span class="token operator">-></span> <span class="token string-literal singleline"><span class="token string">"vnd.android.cursor.dir/vnd.com.example.app.provider.table1"</span></span></pre></td></tr><tr><td data-num="5"></td><td><pre>        table1Item <span class="token operator">-></span> <span class="token string-literal singleline"><span class="token string">"vnd.android.cursor.item/vnd.com.example.app.provider.table1"</span></span></pre></td></tr><tr><td data-num="6"></td><td><pre>        table2Dir <span class="token operator">-></span> <span class="token string-literal singleline"><span class="token string">"vnd.android.cursor.dir/vnd.com.example.app.provider.table2"</span></span></pre></td></tr><tr><td data-num="7"></td><td><pre>        table2Item <span class="token operator">-></span> <span class="token string-literal singleline"><span class="token string">"vnd.android.cursor.item/vnd.com.example.app.provider.table2"</span></span></pre></td></tr><tr><td data-num="8"></td><td><pre>        <span class="token keyword">else</span> <span class="token operator">-></span> <span class="token keyword">null</span></pre></td></tr><tr><td data-num="9"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="10"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="11"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">34</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
<li>
<div class="highlight-container" data-rel="Java"><figure class="iseeu highlight java"><figcaption data-lang="java"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token class-name">Thread</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre>    </pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token punctuation">&#125;</span><span class="token punctuation">.</span><span class="token function">start</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">35</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
</ul>
<h4 id="解析异步消息处理机制"><a class="markdownIt-Anchor" href="#解析异步消息处理机制">#</a> 解析异步消息处理机制</h4>
<ul>
<li>Message
<ul>
<li>Message 是在多线程之间传递的消息，他可以在内部携带少量的信息，用于在不同的线程之间传递数据，除了上面使用的 <code>what</code>  字段，还可以使用 <code>arg1</code>  和 <code>arg2</code>  字段来携带一些整型，使用 <code>obj</code>  字段携带一个 Object 对象</li>
</ul>
</li>
<li>Handler
<ul>
<li>Handler 顾名思义就是处理者的意思，它主要用于发送和处理消息，发送消息一般是使用 Handler 的 <code>sendMessage()</code>  方法、 <code>post()</code>  方法等，而发出的消息经过一系列的辗转处理后，最终会传递到 Handelr 的 <code>handleMessage()</code>  方法中</li>
</ul>
</li>
<li>MessageQueue
<ul>
<li>MessageQueue 是消息队列的意思，它主要用于存放所有通过 Handelr 发送的消息，这部分消息会一直存放在消息队列中，等待被处理，每隔线程中只会有一个 MessageQueue 对象</li>
</ul>
</li>
<li>Looper
<ul>
<li>Looper 是每个线程中的 MessageQueue 的管家，调用 Looper 的 loop () 方法后，就会进入一个无限循环中，然后每当发现 MessageQueue 中存在一条消息时，就回将它取出，并传递到 Handler 的 <code>handleMessage()</code>  方法中，每个线程中只会有一个 Looper 对象</li>
</ul>
</li>
<li>首先需要在主线程当中创建一个 Handler 对象，并重写 handleMessage () 方法。然后当子线程中需要进行 UI 操作时，就创建一个 Message 对象，并通过 Handler 将这条消息发送出去。之后这条消息会被添加到 MessageQueue 的队列中等待被处理，而 Looper 则会一直尝试从 MessageQueue 中取出待处理消息，最后分发回 Handler 的 handleMessage () 方法中。由于 Handler 的构造函数中我们传入了 Looper.getMainLooper ()，所以此时 handleMessage () 方法中的代码也会在主线程中运行，于是我们在这里就可以安心地进行 UI 操作了</li>
<li><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://tuchuang.wenhe9.cn/image-20210926173456241.png"
                      alt="image-20210926173456241"
                ></li>
</ul>
<h4 id="使用asynctask"><a class="markdownIt-Anchor" href="#使用asynctask">#</a> 使用 AsyncTask</h4>
<ul>
<li>
<p>被弃用了</p>
</li>
<li>
<p>首先来看一下 AsyncTask 的基本用法。由于 AsyncTask 是一个抽象类，所以如果我们想使用它，就必须创建一个子类去继承它。在继承时我们可以为 AsyncTask 类指定 3 个泛型参数，这 3 个参数的用途如下</p>
</li>
<li>
<p>Params。在执行 AsyncTask 时需要传入的参数，可用于在后台任务中使用。</p>
</li>
<li>
<p>Progress。在后台任务执行时，如果需要在界面上显示当前的进度，则使用这里指定的泛型作为进度单位。</p>
</li>
<li>
<p>Result。当任务执行完毕后，如果需要对结果进行返回，则使用这里指定的泛型作为返回值类型。</p>
</li>
<li>
<p>因此，一个最简单的自定义 AsyncTask 就可以写成如下形式：</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">class</span> DownloadTask <span class="token operator">:</span> AsyncTask<span class="token operator">&lt;</span>Unit<span class="token punctuation">,</span> Int<span class="token punctuation">,</span> Boolean<span class="token operator">></span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre>    <span class="token operator">..</span><span class="token punctuation">.</span></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">36</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
</ul>
<h3 id="service基本用法"><a class="markdownIt-Anchor" href="#service基本用法">#</a> Service 基本用法</h3>
<ul>
<li>新建一个类继承 Service 类，重写相应的方法
<ul>
<li><code>onBind()</code>
<ul>
<li>必须在子类中实现</li>
</ul>
</li>
<li><code>onCreate()</code>
<ul>
<li>在 Service 创建的时候调用</li>
</ul>
</li>
<li><code>onStartCommand()</code>
<ul>
<li>会在每次 Service 启动的时候调用</li>
<li>如果希望 Service 一旦启动就立刻执行某个动作</li>
</ul>
</li>
<li><code>onDestory()</code>
<ul>
<li>会在 Service 销毁的时候调用</li>
<li>回收那些不再使用的资源</li>
</ul>
</li>
</ul>
</li>
</ul>
<h4 id="启动和停止service"><a class="markdownIt-Anchor" href="#启动和停止service">#</a> 启动和停止 Service</h4>
<ul>
<li>
<p>启动和挺值得方法也是借助 <code>Intent</code>  实现的</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">package</span> cn<span class="token punctuation">.</span>wenhe9<span class="token punctuation">.</span>testmenu</pre></td></tr><tr><td data-num="2"></td><td><pre></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token keyword">import</span> android<span class="token punctuation">.</span>content<span class="token punctuation">.</span>Intent</pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token keyword">import</span> androidx<span class="token punctuation">.</span>appcompat<span class="token punctuation">.</span>app<span class="token punctuation">.</span>AppCompatActivity</pre></td></tr><tr><td data-num="5"></td><td><pre><span class="token keyword">import</span> android<span class="token punctuation">.</span>os<span class="token punctuation">.</span>Bundle</pre></td></tr><tr><td data-num="6"></td><td><pre><span class="token keyword">import</span> cn<span class="token punctuation">.</span>wenhe9<span class="token punctuation">.</span>testmenu<span class="token punctuation">.</span>databinding<span class="token punctuation">.</span>ActivityTestServiceBinding</pre></td></tr><tr><td data-num="7"></td><td><pre></pre></td></tr><tr><td data-num="8"></td><td><pre><span class="token keyword">class</span> TestService <span class="token operator">:</span> <span class="token function">AppCompatActivity</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="9"></td><td><pre></pre></td></tr><tr><td data-num="10"></td><td><pre>    <span class="token keyword">private</span> <span class="token keyword">lateinit</span> <span class="token keyword">var</span> binding <span class="token operator">:</span> ActivityTestServiceBinding</pre></td></tr><tr><td data-num="11"></td><td><pre></pre></td></tr><tr><td data-num="12"></td><td><pre>    <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">onCreate</span><span class="token punctuation">(</span>savedInstanceState<span class="token operator">:</span> Bundle<span class="token operator">?</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="13"></td><td><pre>        <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">onCreate</span><span class="token punctuation">(</span>savedInstanceState<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="14"></td><td><pre>        binding <span class="token operator">=</span> ActivityTestServiceBinding<span class="token punctuation">.</span><span class="token function">inflate</span><span class="token punctuation">(</span>layoutInflater<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="15"></td><td><pre>        <span class="token function">setContentView</span><span class="token punctuation">(</span>binding<span class="token punctuation">.</span>root<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="16"></td><td><pre></pre></td></tr><tr><td data-num="17"></td><td><pre>        binding<span class="token punctuation">.</span>startService<span class="token punctuation">.</span><span class="token function">setOnClickListener</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="18"></td><td><pre>            <span class="token keyword">val</span> intent <span class="token operator">=</span> <span class="token function">Intent</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">,</span> MyService<span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">.</span>java<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="19"></td><td><pre></pre></td></tr><tr><td data-num="20"></td><td><pre>            <span class="token function">startService</span><span class="token punctuation">(</span>intent<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="21"></td><td><pre>        <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="22"></td><td><pre></pre></td></tr><tr><td data-num="23"></td><td><pre>        binding<span class="token punctuation">.</span>stopService<span class="token punctuation">.</span><span class="token function">setOnClickListener</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="24"></td><td><pre>            <span class="token keyword">val</span> intent <span class="token operator">=</span> <span class="token function">Intent</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">,</span> MyService<span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">.</span>java<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="25"></td><td><pre></pre></td></tr><tr><td data-num="26"></td><td><pre>            <span class="token function">stopService</span><span class="token punctuation">(</span>intent<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="27"></td><td><pre>        <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="28"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="29"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="30"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">37</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">package</span> cn<span class="token punctuation">.</span>wenhe9<span class="token punctuation">.</span>testmenu</pre></td></tr><tr><td data-num="2"></td><td><pre></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token keyword">import</span> android<span class="token punctuation">.</span>content<span class="token punctuation">.</span>ComponentName</pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token keyword">import</span> android<span class="token punctuation">.</span>content<span class="token punctuation">.</span>Context</pre></td></tr><tr><td data-num="5"></td><td><pre><span class="token keyword">import</span> android<span class="token punctuation">.</span>content<span class="token punctuation">.</span>Intent</pre></td></tr><tr><td data-num="6"></td><td><pre><span class="token keyword">import</span> android<span class="token punctuation">.</span>content<span class="token punctuation">.</span>ServiceConnection</pre></td></tr><tr><td data-num="7"></td><td><pre><span class="token keyword">import</span> androidx<span class="token punctuation">.</span>appcompat<span class="token punctuation">.</span>app<span class="token punctuation">.</span>AppCompatActivity</pre></td></tr><tr><td data-num="8"></td><td><pre><span class="token keyword">import</span> android<span class="token punctuation">.</span>os<span class="token punctuation">.</span>Bundle</pre></td></tr><tr><td data-num="9"></td><td><pre><span class="token keyword">import</span> android<span class="token punctuation">.</span>os<span class="token punctuation">.</span>IBinder</pre></td></tr><tr><td data-num="10"></td><td><pre><span class="token keyword">import</span> cn<span class="token punctuation">.</span>wenhe9<span class="token punctuation">.</span>testmenu<span class="token punctuation">.</span>databinding<span class="token punctuation">.</span>ActivityTestServiceBinding</pre></td></tr><tr><td data-num="11"></td><td><pre></pre></td></tr><tr><td data-num="12"></td><td><pre><span class="token keyword">class</span> TestService <span class="token operator">:</span> <span class="token function">AppCompatActivity</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="13"></td><td><pre></pre></td></tr><tr><td data-num="14"></td><td><pre>    <span class="token keyword">private</span> <span class="token keyword">lateinit</span> <span class="token keyword">var</span> binding <span class="token operator">:</span> ActivityTestServiceBinding</pre></td></tr><tr><td data-num="15"></td><td><pre></pre></td></tr><tr><td data-num="16"></td><td><pre>    <span class="token keyword">private</span> <span class="token keyword">lateinit</span> <span class="token keyword">var</span> downloadBinder <span class="token operator">:</span> MyService<span class="token punctuation">.</span>DownLoadBinder</pre></td></tr><tr><td data-num="17"></td><td><pre></pre></td></tr><tr><td data-num="18"></td><td><pre>    <span class="token keyword">private</span> <span class="token keyword">val</span> connection <span class="token operator">=</span> <span class="token keyword">object</span> <span class="token operator">:</span> ServiceConnection<span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="19"></td><td><pre>        <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">onServiceConnected</span><span class="token punctuation">(</span>name<span class="token operator">:</span> ComponentName<span class="token operator">?</span><span class="token punctuation">,</span> service<span class="token operator">:</span> IBinder<span class="token operator">?</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="20"></td><td><pre>            downloadBinder <span class="token operator">=</span> service <span class="token keyword">as</span> MyService<span class="token punctuation">.</span>DownLoadBinder</pre></td></tr><tr><td data-num="21"></td><td><pre></pre></td></tr><tr><td data-num="22"></td><td><pre>            downloadBinder<span class="token punctuation">.</span><span class="token function">startDownload</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="23"></td><td><pre>            downloadBinder<span class="token punctuation">.</span><span class="token function">getProgess</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="24"></td><td><pre>        <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="25"></td><td><pre></pre></td></tr><tr><td data-num="26"></td><td><pre>        <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">onServiceDisconnected</span><span class="token punctuation">(</span>name<span class="token operator">:</span> ComponentName<span class="token operator">?</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="27"></td><td><pre>            <span class="token function">TODO</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"Not yet implemented"</span></span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="28"></td><td><pre>        <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="29"></td><td><pre></pre></td></tr><tr><td data-num="30"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="31"></td><td><pre></pre></td></tr><tr><td data-num="32"></td><td><pre>    <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">onCreate</span><span class="token punctuation">(</span>savedInstanceState<span class="token operator">:</span> Bundle<span class="token operator">?</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="33"></td><td><pre>        <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">onCreate</span><span class="token punctuation">(</span>savedInstanceState<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="34"></td><td><pre>        binding <span class="token operator">=</span> ActivityTestServiceBinding<span class="token punctuation">.</span><span class="token function">inflate</span><span class="token punctuation">(</span>layoutInflater<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="35"></td><td><pre>        <span class="token function">setContentView</span><span class="token punctuation">(</span>binding<span class="token punctuation">.</span>root<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="36"></td><td><pre></pre></td></tr><tr><td data-num="37"></td><td><pre>        binding<span class="token punctuation">.</span>startService<span class="token punctuation">.</span><span class="token function">setOnClickListener</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="38"></td><td><pre>            <span class="token keyword">val</span> intent <span class="token operator">=</span> <span class="token function">Intent</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">,</span> MyService<span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">.</span>java<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="39"></td><td><pre></pre></td></tr><tr><td data-num="40"></td><td><pre>            <span class="token function">startService</span><span class="token punctuation">(</span>intent<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="41"></td><td><pre>        <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="42"></td><td><pre></pre></td></tr><tr><td data-num="43"></td><td><pre>        binding<span class="token punctuation">.</span>stopService<span class="token punctuation">.</span><span class="token function">setOnClickListener</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="44"></td><td><pre>            <span class="token keyword">val</span> intent <span class="token operator">=</span> <span class="token function">Intent</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">,</span> MyService<span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">.</span>java<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="45"></td><td><pre></pre></td></tr><tr><td data-num="46"></td><td><pre>            <span class="token function">stopService</span><span class="token punctuation">(</span>intent<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="47"></td><td><pre>        <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="48"></td><td><pre></pre></td></tr><tr><td data-num="49"></td><td><pre>        binding<span class="token punctuation">.</span>bindServiceBtn<span class="token punctuation">.</span><span class="token function">setOnClickListener</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="50"></td><td><pre>            <span class="token keyword">val</span> intent <span class="token operator">=</span> <span class="token function">Intent</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">,</span> MyService<span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">.</span>java<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="51"></td><td><pre>            <span class="token function">bindService</span><span class="token punctuation">(</span>intent<span class="token punctuation">,</span> connection<span class="token punctuation">,</span> Context<span class="token punctuation">.</span>BIND_AUTO_CREATE<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="52"></td><td><pre>        <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="53"></td><td><pre></pre></td></tr><tr><td data-num="54"></td><td><pre>        binding<span class="token punctuation">.</span>unBindServiceBtn<span class="token punctuation">.</span><span class="token function">setOnClickListener</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="55"></td><td><pre>            <span class="token function">unbindService</span><span class="token punctuation">(</span>connection<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="56"></td><td><pre>        <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="57"></td><td><pre></pre></td></tr><tr><td data-num="58"></td><td><pre></pre></td></tr><tr><td data-num="59"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="60"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="61"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">38</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr><tr><td data-num="62"></td><td><pre></pre></td></tr><tr><td data-num="63"></td><td><pre><span class="token operator">-</span> 可以看到这就是使用的通知的写法，只是在租后没有使用``<span class="token function">notigy</span><span class="token punctuation">(</span><span class="token punctuation">)</span>``将通知显示出来，而是使用``<span class="token function">startForeground</span><span class="token punctuation">(</span><span class="token punctuation">)</span>``方法，这方法接收两个参数：</pre></td></tr><tr><td data-num="64"></td><td><pre>  <span class="token operator">-</span> 第一个参数是通知的id，类似与``<span class="token function">notify</span><span class="token punctuation">(</span><span class="token punctuation">)</span>``方法的第一个参数，唯一即可</pre></td></tr><tr><td data-num="65"></td><td><pre>  <span class="token operator">-</span> 第二个参数则是构建的``Notification``对象</pre></td></tr><tr><td data-num="66"></td><td><pre><span class="token operator">-</span> 调用``<span class="token function">startForeground</span><span class="token punctuation">(</span><span class="token punctuation">)</span>``方法后就会让MyService变成一个前台Service，并在系统状态栏显示出来</pre></td></tr></table></figure></div></li>
<li>
<p>另外从 Android 9.0 系统开始，使用前台 Service 必须在 AndroidManifest.xml 文件中进行权限声明才行：</p>
<ul>
<li>
<div class="highlight-container" data-rel="Xml"><figure class="iseeu highlight xml"><figcaption data-lang="XML"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>manifest</span> <span class="token attr-name"><span class="token namespace">xmlns:</span>android</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://schemas.android.com/apk/res/android<span class="token punctuation">"</span></span></pre></td></tr><tr><td data-num="2"></td><td><pre>          <span class="token attr-name">package</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>com.example.servicetest<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="3"></td><td><pre>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>uses-permission</span> <span class="token attr-name"><span class="token namespace">android:</span>name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>android.permission.FOREGROUND_SERVICE<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></pre></td></tr><tr><td data-num="4"></td><td><pre>    ...</pre></td></tr><tr><td data-num="5"></td><td><pre><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>manifest</span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="6"></td><td><pre><span class="token comment"><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">#### 使用IntentService</span><br><span class="line"></span><br><span class="line">- 在一开始我们就知道，Service中的代码都是默认运行在主线程当中的，如果直接在Service里处理一些耗时的逻辑，就很容易出现ANR（Application Not Responding）的情况</span><br><span class="line"></span><br><span class="line">- 所以这个后就需要用到Android多线程的技术了，我们应该在Service的每隔具体的方法里开启一个子线程，然后在这里处理那些耗时的的逻辑，因此一个标准的Service就可以携程如下形式：</span><br><span class="line"></span><br><span class="line">  - ```kotlin</span><br><span class="line">    class MyService : Service() &#123;</span><br><span class="line">        ...</span><br><span class="line">        override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int &#123;</span><br><span class="line">            thread &#123;</span><br><span class="line">                // 处理具体的逻辑</span><br><span class="line">            &#125;</span><br><span class="line">            return super.onStartCommand(intent, flags, startId)</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></div></div></span></pre></td></tr></table></figure></li>
</ul>
</li>
<li>
<p>但是，这种 Service 一旦启动，就会一直处于运行状态，必须调用 <code>stopService()</code>  或 <code>stioSelf()</code>  方法或者被系统回收，Service 才会停止，所以，如果想要实现让一个 Service 在执行完毕后自动停止的，就可以这样写：</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">class</span> MyService <span class="token operator">:</span> <span class="token function">Service</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre>    <span class="token operator">..</span><span class="token punctuation">.</span></pre></td></tr><tr><td data-num="3"></td><td><pre>    <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">onStartCommand</span><span class="token punctuation">(</span>intent<span class="token operator">:</span> Intent<span class="token punctuation">,</span> flags<span class="token operator">:</span> Int<span class="token punctuation">,</span> startId<span class="token operator">:</span> Int<span class="token punctuation">)</span><span class="token operator">:</span> Int <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="4"></td><td><pre>        thread <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="5"></td><td><pre>            <span class="token comment">// 处理具体的逻辑</span></pre></td></tr><tr><td data-num="6"></td><td><pre>            <span class="token function">stopSelf</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="7"></td><td><pre>        <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="8"></td><td><pre>        <span class="token keyword">return</span> <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">onStartCommand</span><span class="token punctuation">(</span>intent<span class="token punctuation">,</span> flags<span class="token punctuation">,</span> startId<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="9"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="10"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="11"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">40</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>这里首先要求必须先调用父类的构造函数，并传入一个字符串，这个字符串可以随意指定，只在调试的时候有用。然后要在子类中实现 <code>onHandleIntent()</code>  这个抽象方法，这个方法中可以处理一些耗时的逻辑，而不用担心 ANR 的问题，因为这个方法已经是在子线程中运行的了。这里为了证实一下，我们在 <code>onHandleIntent()</code>  方法中打印了当前线程名。另外，根据 IntentService 的特性，这个 Service 在运行结束后应该是会自动停止的，所以我们又重写了 <code>onDestroy()</code>  方法，在这里也打印了一行日志，以证实 Service 是不是停止了。</p>
</li>
</ul>
<h2 id="多媒体"><a class="markdownIt-Anchor" href="#多媒体">#</a> 多媒体</h2>
<h3 id="使用通知"><a class="markdownIt-Anchor" href="#使用通知">#</a> 使用通知</h3>
<ul>
<li>通知 (notification) 是 anndroid 系统中比较有特色的一个功能，当某个应用程序希望向用户发出一些提示信息，而该应用程序又不在前台运行时，就可以借助通知来实现，发出一条通知后，手机最上方的状态栏中会显示一个通知的图标，下拉状态栏后可以看到通知的详细内容</li>
</ul>
<h4 id="创建通知渠道"><a class="markdownIt-Anchor" href="#创建通知渠道">#</a> 创建通知渠道</h4>
<ul>
<li>
<p>什么是通知渠道</p>
<ul>
<li>就是每一条通知都要属于一个对应的渠道，每个应用程序都可以自由的创建当前应用拥有哪些通知渠道，但是这些通知渠道的控制权是掌握在用户手上的，用户可以自由的选择这些同志渠道的重要程度，是否响铃、是否震动或者是否要关闭这个渠道的通知</li>
</ul>
</li>
<li>
<p>创建通知渠道的步骤</p>
<ul>
<li>
<p>首先需要一个 <code>NotificationManager</code>  对通知进行管理，可以通过调用 <code>Cotnext</code>  的 <code>getSystemService()</code>  方法获取。 <code>getSystemService()</code>  方法接收一个字符串参数用于确定获取系统的哪个服务，这里我们传入 <code>Contxt.NOTIFICATION_SERCICE</code>  即可，因此，获取 <code>NotificationManager</code>  的实例既可以写成：</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">val</span> manager <span class="token function">getSystenService</span><span class="token punctuation">(</span>Context<span class="token punctuation">.</span>NOTIFICATION_SERICE<span class="token punctuation">)</span> <span class="token keyword">as</span> NotificationManager</pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">41</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>创建一个通知渠道至少需要渠道 ID、渠道名称以及重要等级这三个参数</p>
<ul>
<li>渠道 ID 可以随便定义，只要保证全局唯一性就可以</li>
<li>渠道名称是给用户看的，需要清楚地表达这个渠道的用途</li>
<li>通知的重要等级主要 <code>IMPORTANCE_HIGH</code> 、 <code>IMPORTANCE_DEFAULT</code> 、 <code>IMPORTANCE_LOW</code> 、 <code>IMPORTANCE_MIN</code>  这几种，对应的重要程度依次从高到低，不同的重要等机会决定通知的不同行为，当然这里只是初始状态下的重要等级，用户可以随时手动更改某个通知渠道的重要等级，开发者是无法干预的</li>
</ul>
</li>
</ul>
</li>
</ul>
<h4 id="通知的基本用法"><a class="markdownIt-Anchor" href="#通知的基本用法">#</a> 通知的基本用法</h4>
<ul>
<li>
<p>通知 的用法还是比较灵活的，既可以在 Activity 里创建，也可以在 BroadcastRecevier 中床架你，当然还可以在 Service 里创建，相比于 BroadcastReceiver 和 Service，在 Activity 里创建通知的场景是比较少的，因为一般只有程序进入后台的时候才需要使用通知</p>
</li>
<li>
<p>创建通知的步骤：</p>
<ul>
<li>
<p>首先需要使用一个 Builder 构造器来创建 <code>Notification</code>  对象，但问题在于，Android 系统的没有个版本都会对通知功能进行或多或少的修改，API 不稳定的问题在通知上凸显的尤为严重，解决办法就是使用 AndroidX 库中提供的兼容 API，AndroidX 库中提供了一个 <code>NotificationCompat</code>  类，使用这个类的构造器创建 <code>Notificatin</code>  对象，就可以保证我们的程序在所有 Android 系统版本上都能正常工作了，代码如下：</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">val</span> notification <span class="token operator">=</span> NotificationCompat<span class="token punctuation">.</span><span class="token function">Builder</span><span class="token punctuation">(</span>contxext<span class="token punctuation">,</span> channelId<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">42</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr><tr><td data-num="3"></td><td><pre></pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token operator">-</span> ``<span class="token function">setContentTitle</span><span class="token punctuation">(</span><span class="token punctuation">)</span>``方法用于指定通知的标题内容，下拉系统状态来就可以看到这部分内容</pre></td></tr><tr><td data-num="5"></td><td><pre><span class="token operator">-</span> ``<span class="token function">setConentText</span><span class="token punctuation">(</span><span class="token punctuation">)</span>``方法用于指定通知的正文内容，同样下来系统装天蓝就可以看到这部分内容</pre></td></tr><tr><td data-num="6"></td><td><pre><span class="token operator">-</span> ``<span class="token function">setSmallIcon</span><span class="token punctuation">(</span><span class="token punctuation">)</span>``方法用于设置通知的小图标，注意，只能使用<span class="token operator">*</span><span class="token operator">*</span>纯aplha<span class="token operator">*</span><span class="token operator">*</span>图层的图片进行设置，小图标会显示在系统状态栏</pre></td></tr><tr><td data-num="7"></td><td><pre><span class="token operator">-</span> ``<span class="token function">setlargeIcon</span><span class="token punctuation">(</span><span class="token punctuation">)</span>``方法用于设置通知的大图标，当下拉系统状态栏就可以看到设置的大图标了</pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>最后只需要调用 <code>NotificationManager</code>  的 <code>notify()</code>  方法就可以让通知显示出来了， <code>notify()</code>  方法接收这两个参数：</p>
<ul>
<li>
<p>第一个参数是 Id，要保证为每个通知指定的 id 都是不同的</p>
</li>
<li>
<p>第二个参数则是 Notification 对昂，这里直接将创建好的 Notification 对象传入即可</p>
</li>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre>manager<span class="token punctuation">.</span><span class="token function">notify</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> notification<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">43</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>此时，可以看到，虽然跳转界面了，但是系统状态栏的通知图标还没有消息，这是因为我们没有在代码中对该同志进行取消，他就会一直显示在系统的状态栏上，解决方法有两种</p>
<ul>
<li>
<p>一种是在 <code>NotificationCompat.Builder()</code>  中再连缀一个 <code>setAutoCancel()</code>  方法、</p>
</li>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">val</span> notification <span class="token operator">=</span> NotificationCompat<span class="token punctuation">.</span><span class="token function">Builder</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">,</span> <span class="token string-literal singleline"><span class="token string">"normal"</span></span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="2"></td><td><pre>        <span class="token operator">..</span><span class="token punctuation">.</span></pre></td></tr><tr><td data-num="3"></td><td><pre>        <span class="token punctuation">.</span><span class="token function">setAutoCancel</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="4"></td><td><pre>        <span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="5"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">44</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
<li>
<p>注意，在 <code>cancel()</code>  方法中传入了 1，这个 1 是我么在创建通知的时候给每条通知指定的 id，取消哪条通知，在 <code>cancel()</code>  方法中传入该通知的 id 就可以了</p>
</li>
</ul>
</li>
</ul>
</li>
<li>
<p><code>PendingIntent</code>  和 <code>Intent</code>  的区别和联系</p>
<ul>
<li>联系
<ul>
<li>他们都可以指明某一个 “意图”，都可以用于启动 Activity、启动 Service 以及发送广播</li>
</ul>
</li>
<li>区别
<ul>
<li><code>Intent</code>  倾向于立即执行某个动作</li>
<li>而 <code>PendingIntent</code>  倾向于某个合适的时机执行某个动作</li>
</ul>
</li>
<li>所以，也可以把 <code>PendintIntent</code>  简单地理解为延迟执行的 <code>Intent</code></li>
</ul>
</li>
</ul>
<h4 id="通知的进阶技巧"><a class="markdownIt-Anchor" href="#通知的进阶技巧">#</a> 通知的进阶技巧</h4>
<ul>
<li>
<p><code>setStyle()</code></p>
<ul>
<li>
<p>这个方法允许我们构造出富文本的内容，也就是说，通知中不光可以有文字和图标，还可以包含更多的东西， <code>setStyle()</code>  方法接收一个 <code>NotificationCompat.style</code>  参数，这个参数就是用来构造具体的富文本信息的，如文字、图片</p>
</li>
<li>
<p>在通知中显示一段长文</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">val</span> notification <span class="token operator">=</span> NotificationCompat<span class="token punctuation">.</span><span class="token function">Builder</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">,</span> <span class="token string-literal singleline"><span class="token string">"test_notify"</span></span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token punctuation">.</span><span class="token function">setStyle</span><span class="token punctuation">(</span>NotificationCompat<span class="token punctuation">.</span><span class="token function">BigTextStyle</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">bigText</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"nihaodadjiaosjdklasjdlkajskldjakldjialjdklajdkljaskldjlkasjdklasjdkljaskldjaljdklasjdkljakjdakljl"</span></span><span class="token punctuation">)</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">45</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
</ul>
</li>
<li>
<p>不同重要的顶级的通知渠道对通知的行为具体有什么影响</p>
<ul>
<li>其实简单来讲，就是通知渠道的重要等级越高，发出的通知就越容易获得用户的注意。比如高重要等级的通知渠道发出的通知可以弹出横幅、发出声音，而低重要等级的通知渠道发出的通知不仅可能会在某些情况下被隐藏，而且可能会被改变显示的顺序，将其排在更重要的通知之后。</li>
<li>但需要注意的是，开发者只能在创建通知渠道的时候为它指定初始的重要等级，如果用户不认可这个重要等级的话，可以随时进行修改，开发者对此无权再进行调整和变更，因为通知渠道一旦创建就不能再通过代码修改了。</li>
</ul>
</li>
</ul>
<h3 id="调用摄像头和相册"><a class="markdownIt-Anchor" href="#调用摄像头和相册">#</a> 调用摄像头和相册</h3>
<ul>
<li>
<p>调用摄像头拍照</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">class</span> MainActivity <span class="token operator">:</span> <span class="token function">AppCompatActivity</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre></pre></td></tr><tr><td data-num="3"></td><td><pre>    <span class="token keyword">val</span> takePhoto <span class="token operator">=</span> <span class="token number">1</span></pre></td></tr><tr><td data-num="4"></td><td><pre>    <span class="token keyword">lateinit</span> <span class="token keyword">var</span> imageUri<span class="token operator">:</span> Uri</pre></td></tr><tr><td data-num="5"></td><td><pre>    <span class="token keyword">lateinit</span> <span class="token keyword">var</span> outputImage<span class="token operator">:</span> File</pre></td></tr><tr><td data-num="6"></td><td><pre></pre></td></tr><tr><td data-num="7"></td><td><pre>    <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">onCreate</span><span class="token punctuation">(</span>savedInstanceState<span class="token operator">:</span> Bundle<span class="token operator">?</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="8"></td><td><pre>        <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">onCreate</span><span class="token punctuation">(</span>savedInstanceState<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="9"></td><td><pre>        <span class="token function">setContentView</span><span class="token punctuation">(</span>R<span class="token punctuation">.</span>layout<span class="token punctuation">.</span>activity_main<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="10"></td><td><pre>        takePhotoBtn<span class="token punctuation">.</span><span class="token function">setOnClickListener</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="11"></td><td><pre>            <span class="token comment">// 创建 File 对象，用于存储拍照后的图片</span></pre></td></tr><tr><td data-num="12"></td><td><pre>            outputImage <span class="token operator">=</span> <span class="token function">File</span><span class="token punctuation">(</span>externalCacheDir<span class="token punctuation">,</span> <span class="token string-literal singleline"><span class="token string">"output_image.jpg"</span></span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="13"></td><td><pre>            <span class="token keyword">if</span> <span class="token punctuation">(</span>outputImage<span class="token punctuation">.</span><span class="token function">exists</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="14"></td><td><pre>                outputImage<span class="token punctuation">.</span><span class="token function">delete</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="15"></td><td><pre>            <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="16"></td><td><pre>            outputImage<span class="token punctuation">.</span><span class="token function">createNewFile</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="17"></td><td><pre>            imageUri <span class="token operator">=</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>Build<span class="token punctuation">.</span>VERSION<span class="token punctuation">.</span>SDK_INT <span class="token operator">>=</span> Build<span class="token punctuation">.</span>VERSION_CODES<span class="token punctuation">.</span>N<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="18"></td><td><pre>                FileProvider<span class="token punctuation">.</span><span class="token function">getUriForFile</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">,</span> "com<span class="token punctuation">.</span>example<span class="token punctuation">.</span>cameraalbumtest<span class="token punctuation">.</span></pre></td></tr><tr><td data-num="19"></td><td><pre>                    fileprovider"<span class="token punctuation">,</span> outputImage<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="20"></td><td><pre>            <span class="token punctuation">&#125;</span> <span class="token keyword">else</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="21"></td><td><pre>                Uri<span class="token punctuation">.</span><span class="token function">fromFile</span><span class="token punctuation">(</span>outputImage<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="22"></td><td><pre>            <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="23"></td><td><pre>            <span class="token comment">// 启动相机程序</span></pre></td></tr><tr><td data-num="24"></td><td><pre>            <span class="token keyword">val</span> intent <span class="token operator">=</span> <span class="token function">Intent</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"android.media.action.IMAGE_CAPTURE"</span></span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="25"></td><td><pre>            intent<span class="token punctuation">.</span><span class="token function">putExtra</span><span class="token punctuation">(</span>MediaStore<span class="token punctuation">.</span>EXTRA_OUTPUT<span class="token punctuation">,</span> imageUri<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="26"></td><td><pre>            <span class="token function">startActivityForResult</span><span class="token punctuation">(</span>intent<span class="token punctuation">,</span> takePhoto<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="27"></td><td><pre>        <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="28"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="29"></td><td><pre></pre></td></tr><tr><td data-num="30"></td><td><pre>    <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">onActivityResult</span><span class="token punctuation">(</span>requestCode<span class="token operator">:</span> Int<span class="token punctuation">,</span> resultCode<span class="token operator">:</span> Int<span class="token punctuation">,</span> <span class="token keyword">data</span><span class="token operator">:</span> Intent<span class="token operator">?</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="31"></td><td><pre>        <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">onActivityResult</span><span class="token punctuation">(</span>requestCode<span class="token punctuation">,</span> resultCode<span class="token punctuation">,</span> <span class="token keyword">data</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="32"></td><td><pre>        <span class="token keyword">when</span> <span class="token punctuation">(</span>requestCode<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="33"></td><td><pre>            takePhoto <span class="token operator">-></span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="34"></td><td><pre>                <span class="token keyword">if</span> <span class="token punctuation">(</span>resultCode <span class="token operator">==</span> Activity<span class="token punctuation">.</span>RESULT_OK<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="35"></td><td><pre>                    <span class="token comment">// 将拍摄的照片显示出来</span></pre></td></tr><tr><td data-num="36"></td><td><pre>                    <span class="token keyword">val</span> bitmap <span class="token operator">=</span> BitmapFactory<span class="token punctuation">.</span><span class="token function">decodeStream</span><span class="token punctuation">(</span>contentResolver<span class="token punctuation">.</span></pre></td></tr><tr><td data-num="37"></td><td><pre>                        <span class="token function">openInputStream</span><span class="token punctuation">(</span>imageUri<span class="token punctuation">)</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="38"></td><td><pre>                    imageView<span class="token punctuation">.</span><span class="token function">setImageBitmap</span><span class="token punctuation">(</span><span class="token function">rotateIfRequired</span><span class="token punctuation">(</span>bitmap<span class="token punctuation">)</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="39"></td><td><pre>                <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="40"></td><td><pre>            <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="41"></td><td><pre>        <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="42"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="43"></td><td><pre></pre></td></tr><tr><td data-num="44"></td><td><pre>    <span class="token keyword">private</span> <span class="token keyword">fun</span> <span class="token function">rotateIfRequired</span><span class="token punctuation">(</span>bitmap<span class="token operator">:</span> Bitmap<span class="token punctuation">)</span><span class="token operator">:</span> Bitmap <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="45"></td><td><pre>        <span class="token keyword">val</span> exif <span class="token operator">=</span> <span class="token function">ExifInterface</span><span class="token punctuation">(</span>outputImage<span class="token punctuation">.</span>path<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="46"></td><td><pre>        <span class="token keyword">val</span> orientation <span class="token operator">=</span> exif<span class="token punctuation">.</span><span class="token function">getAttributeInt</span><span class="token punctuation">(</span>ExifInterface<span class="token punctuation">.</span>TAG_ORIENTATION<span class="token punctuation">,</span></pre></td></tr><tr><td data-num="47"></td><td><pre>            ExifInterface<span class="token punctuation">.</span>ORIENTATION_NORMAL<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="48"></td><td><pre>        <span class="token keyword">return</span> <span class="token keyword">when</span> <span class="token punctuation">(</span>orientation<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="49"></td><td><pre>            ExifInterface<span class="token punctuation">.</span>ORIENTATION_ROTATE_90 <span class="token operator">-></span> <span class="token function">rotateBitmap</span><span class="token punctuation">(</span>bitmap<span class="token punctuation">,</span> <span class="token number">90</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="50"></td><td><pre>            ExifInterface<span class="token punctuation">.</span>ORIENTATION_ROTATE_180 <span class="token operator">-></span> <span class="token function">rotateBitmap</span><span class="token punctuation">(</span>bitmap<span class="token punctuation">,</span> <span class="token number">180</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="51"></td><td><pre>            ExifInterface<span class="token punctuation">.</span>ORIENTATION_ROTATE_270 <span class="token operator">-></span> <span class="token function">rotateBitmap</span><span class="token punctuation">(</span>bitmap<span class="token punctuation">,</span> <span class="token number">270</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="52"></td><td><pre>            <span class="token keyword">else</span> <span class="token operator">-></span> bitmap</pre></td></tr><tr><td data-num="53"></td><td><pre>        <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="54"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="55"></td><td><pre></pre></td></tr><tr><td data-num="56"></td><td><pre>    <span class="token keyword">private</span> <span class="token keyword">fun</span> <span class="token function">rotateBitmap</span><span class="token punctuation">(</span>bitmap<span class="token operator">:</span> Bitmap<span class="token punctuation">,</span> degree<span class="token operator">:</span> Int<span class="token punctuation">)</span><span class="token operator">:</span> Bitmap <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="57"></td><td><pre>        <span class="token keyword">val</span> matrix <span class="token operator">=</span> <span class="token function">Matrix</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="58"></td><td><pre>        matrix<span class="token punctuation">.</span><span class="token function">postRotate</span><span class="token punctuation">(</span>degree<span class="token punctuation">.</span><span class="token function">toFloat</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="59"></td><td><pre>        <span class="token keyword">val</span> rotatedBitmap <span class="token operator">=</span> Bitmap<span class="token punctuation">.</span><span class="token function">createBitmap</span><span class="token punctuation">(</span>bitmap<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> bitmap<span class="token punctuation">.</span>width<span class="token punctuation">,</span> bitmap<span class="token punctuation">.</span>height<span class="token punctuation">,</span></pre></td></tr><tr><td data-num="60"></td><td><pre>            matrix<span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="61"></td><td><pre>        bitmap<span class="token punctuation">.</span><span class="token function">recycle</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 将不再需要的 Bitmap 对象回收</span></pre></td></tr><tr><td data-num="62"></td><td><pre>        <span class="token keyword">return</span> rotatedBitmap</pre></td></tr><tr><td data-num="63"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="64"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="65"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">46</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
<li>
<p>android:name 属性的值是固定的，而 android:authorities 属性的值必须和刚才 FileProvider.getUriForFile () 方法中的第二个参数一致。另外，这里还在<provider>标签的内部使用<meta-data>指定 Uri 的共享路径，并引用了一个 @xml/file_paths 资源。当然，这个资源现在还是不存在的，下面我们就来创建它。</p>
</li>
<li>
<p>右击 res 目录→New→Directory，创建一个 xml 目录，接着右击 xml 目录→New→File，创建一个 file_paths.xml 文件。然后修改 file_paths.xml 文件中的内容，如下所示：</p>
<ul>
<li>
<div class="highlight-container" data-rel="Xml"><figure class="iseeu highlight xml"><figcaption data-lang="XML"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token prolog">&lt;?xml version="1.0" encoding="utf-8"?></span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>paths</span> <span class="token attr-name"><span class="token namespace">xmlns:</span>android</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://schemas.android.com/apk/res/android<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="3"></td><td><pre>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>external-path</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>my_images<span class="token punctuation">"</span></span> <span class="token attr-name">path</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>paths</span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="5"></td><td><pre><span class="token comment"><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">  - external-path就是用来指定Uri共享路径的，name属性的值可以随便填，path属性的值表示共享的具体路径。这里使用一个单斜线表示将整个SD卡进行共享，当然你也可以仅共享存放output_image.jpg这张图片的路径。</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">- 从相册中获取图片</span><br><span class="line"></span><br><span class="line">  - ```kotlin</span><br><span class="line">    class MainActivity : AppCompatActivity() &#123;</span><br><span class="line">        ...</span><br><span class="line">        val fromAlbum = 2</span><br><span class="line">    </span><br><span class="line">        override fun onCreate(savedInstanceState: Bundle?) &#123;</span><br><span class="line">            ...</span><br><span class="line">            fromAlbumBtn.setOnClickListener &#123;</span><br><span class="line">                // 打开文件选择器</span><br><span class="line">                val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)</span><br><span class="line">                intent.addCategory(Intent.CATEGORY_OPENABLE)</span><br><span class="line">                // 指定只显示图片</span><br><span class="line">                intent.type = &quot;image/ *&quot;</span><br><span class="line">                startActivityForResult(intent, fromAlbum)</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    </span><br><span class="line">        override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) &#123;</span><br><span class="line">            super.onActivityResult(requestCode, resultCode, data)</span><br><span class="line">            when (requestCode) &#123;</span><br><span class="line">                ...</span><br><span class="line">                fromAlbum -&gt; &#123;</span><br><span class="line">                    if (resultCode == Activity.RESULT_OK &amp;&amp; data != null) &#123;</span><br><span class="line">                        data.data?.let &#123; uri -&gt;</span><br><span class="line">                            // 将选择的图片显示</span><br><span class="line">                            val bitmap = getBitmapFromUri(uri)</span><br><span class="line">                            imageView.setImageBitmap(bitmap)</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    </span><br><span class="line">        private fun getBitmapFromUri(uri: Uri) = contentResolver</span><br><span class="line">            .openFileDescriptor(uri, &quot;r&quot;)?.use &#123;</span><br><span class="line">            BitmapFactory.decodeFileDescriptor(it.fileDescriptor)</span><br><span class="line">        &#125;</span><br><span class="line">        ...</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></div></div></span></pre></td></tr></table></figure></li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="播放多媒体文件"><a class="markdownIt-Anchor" href="#播放多媒体文件">#</a> 播放多媒体文件</h3>
<ul>
<li>
<p>播放音频</p>
<ul>
<li><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://tuchuang.wenhe9.cn/image-20210926164542810.png"
                      alt="image-20210926164542810"
                ></li>
</ul>
</li>
<li>
<p>Android Studio 允许我们在项目工程中创建一个 assets 目录，并在这个目录下存放任意文件和子目录，这些文件和子目录在项目打包时会一并被打包到安装文件中，然后我们在程序中就可以借助 AssetManager 这个类提供的接口对 assets 目录下的文件进行读取。</p>
</li>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">class</span> MainActivity <span class="token operator">:</span> <span class="token function">AppCompatActivity</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre></pre></td></tr><tr><td data-num="3"></td><td><pre>    <span class="token keyword">private</span> <span class="token keyword">val</span> mediaPlayer <span class="token operator">=</span> <span class="token function">MediaPlayer</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="4"></td><td><pre></pre></td></tr><tr><td data-num="5"></td><td><pre>    <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">onCreate</span><span class="token punctuation">(</span>savedInstanceState<span class="token operator">:</span> Bundle<span class="token operator">?</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="6"></td><td><pre>        <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">onCreate</span><span class="token punctuation">(</span>savedInstanceState<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="7"></td><td><pre>        <span class="token function">setContentView</span><span class="token punctuation">(</span>R<span class="token punctuation">.</span>layout<span class="token punctuation">.</span>activity_main<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="8"></td><td><pre>        <span class="token function">initMediaPlayer</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="9"></td><td><pre>        play<span class="token punctuation">.</span><span class="token function">setOnClickListener</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="10"></td><td><pre>            <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>mediaPlayer<span class="token punctuation">.</span>isPlaying<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="11"></td><td><pre>                mediaPlayer<span class="token punctuation">.</span><span class="token function">start</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 开始播放</span></pre></td></tr><tr><td data-num="12"></td><td><pre>            <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="13"></td><td><pre>        <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="14"></td><td><pre>        pause<span class="token punctuation">.</span><span class="token function">setOnClickListener</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="15"></td><td><pre>            <span class="token keyword">if</span> <span class="token punctuation">(</span>mediaPlayer<span class="token punctuation">.</span>isPlaying<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="16"></td><td><pre>                mediaPlayer<span class="token punctuation">.</span><span class="token function">pause</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 暂停播放</span></pre></td></tr><tr><td data-num="17"></td><td><pre>            <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="18"></td><td><pre>        <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="19"></td><td><pre>        stop<span class="token punctuation">.</span><span class="token function">setOnClickListener</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="20"></td><td><pre>            <span class="token keyword">if</span> <span class="token punctuation">(</span>mediaPlayer<span class="token punctuation">.</span>isPlaying<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="21"></td><td><pre>                mediaPlayer<span class="token punctuation">.</span><span class="token function">reset</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 停止播放</span></pre></td></tr><tr><td data-num="22"></td><td><pre>                <span class="token function">initMediaPlayer</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="23"></td><td><pre>            <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="24"></td><td><pre>        <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="25"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="26"></td><td><pre></pre></td></tr><tr><td data-num="27"></td><td><pre>    <span class="token keyword">private</span> <span class="token keyword">fun</span> <span class="token function">initMediaPlayer</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="28"></td><td><pre>        <span class="token keyword">val</span> assetManager <span class="token operator">=</span> assets</pre></td></tr><tr><td data-num="29"></td><td><pre>        <span class="token keyword">val</span> fd <span class="token operator">=</span> assetManager<span class="token punctuation">.</span><span class="token function">openFd</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"music.mp3"</span></span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="30"></td><td><pre>        mediaPlayer<span class="token punctuation">.</span><span class="token function">setDataSource</span><span class="token punctuation">(</span>fd<span class="token punctuation">.</span>fileDescriptor<span class="token punctuation">,</span> fd<span class="token punctuation">.</span>startOffset<span class="token punctuation">,</span> fd<span class="token punctuation">.</span>length<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="31"></td><td><pre>        mediaPlayer<span class="token punctuation">.</span><span class="token function">prepare</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="32"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="33"></td><td><pre></pre></td></tr><tr><td data-num="34"></td><td><pre>    <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">onDestroy</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="35"></td><td><pre>        <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">onDestroy</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="36"></td><td><pre>        mediaPlayer<span class="token punctuation">.</span><span class="token function">stop</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="37"></td><td><pre>        mediaPlayer<span class="token punctuation">.</span><span class="token function">release</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="38"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="39"></td><td><pre></pre></td></tr><tr><td data-num="40"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="41"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">48</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
<h2 id="网络技术"><a class="markdownIt-Anchor" href="#网络技术">#</a> 网络技术</h2>
<h3 id="webview的用法"><a class="markdownIt-Anchor" href="#webview的用法">#</a> WebView 的用法</h3>
<ul>
<li>
<p>当我们的应用程序需要展示一些网页，除了使用系统浏览器外，我们还可以使用 Android 提供的 <code>WebView</code>  控件，借助它实现在自己的应用程序里嵌入一个浏览器</p>
<ul>
<li>
<div class="highlight-container" data-rel="Xml"><figure class="iseeu highlight xml"><figcaption data-lang="XML"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token prolog">&lt;?xml version="1.0" encoding="utf-8"?></span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>LinearLayout</span></pre></td></tr><tr><td data-num="3"></td><td><pre>        <span class="token attr-name"><span class="token namespace">xmlns:</span>android</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://schemas.android.com/apk/res/android<span class="token punctuation">"</span></span></pre></td></tr><tr><td data-num="4"></td><td><pre>        <span class="token attr-name"><span class="token namespace">android:</span>orientation</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>vertical<span class="token punctuation">"</span></span></pre></td></tr><tr><td data-num="5"></td><td><pre>        <span class="token attr-name"><span class="token namespace">android:</span>layout_width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>match_parent<span class="token punctuation">"</span></span></pre></td></tr><tr><td data-num="6"></td><td><pre>        <span class="token attr-name"><span class="token namespace">android:</span>layout_height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>match_parent<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="7"></td><td><pre></pre></td></tr><tr><td data-num="8"></td><td><pre></pre></td></tr><tr><td data-num="9"></td><td><pre>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>WebView</span></pre></td></tr><tr><td data-num="10"></td><td><pre>            <span class="token attr-name"><span class="token namespace">android:</span>id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>@+id/webView<span class="token punctuation">"</span></span></pre></td></tr><tr><td data-num="11"></td><td><pre>            <span class="token attr-name"><span class="token namespace">android:</span>layout_width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>match_parent<span class="token punctuation">"</span></span></pre></td></tr><tr><td data-num="12"></td><td><pre>            <span class="token attr-name"><span class="token namespace">android:</span>layout_height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>match_parent<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span></pre></td></tr><tr><td data-num="13"></td><td><pre></pre></td></tr><tr><td data-num="14"></td><td><pre><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>LinearLayout</span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="15"></td><td><pre><span class="token comment"><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">- ```kotlin</span><br><span class="line">  package cn.wenhe9.testmenu</span><br><span class="line">  </span><br><span class="line">  import androidx.appcompat.app.AppCompatActivity</span><br><span class="line">  import android.os.Bundle</span><br><span class="line">  import android.webkit.WebView</span><br><span class="line">  import android.webkit.WebViewClient</span><br><span class="line">  import cn.wenhe9.testmenu.databinding.ActivityTestWebViewBinding</span><br><span class="line">  </span><br><span class="line">  class TestWebView : AppCompatActivity() &#123;</span><br><span class="line">  </span><br><span class="line">      private lateinit var binding : ActivityTestWebViewBinding</span><br><span class="line">  </span><br><span class="line">      override fun onCreate(savedInstanceState: Bundle?) &#123;</span><br><span class="line">          super.onCreate(savedInstanceState)</span><br><span class="line">          binding = ActivityTestWebViewBinding.inflate(layoutInflater)</span><br><span class="line">          setContentView(binding.root)</span><br><span class="line">  </span><br><span class="line">          val webView = binding.webView as WebView</span><br><span class="line">  </span><br><span class="line">          webView.settings.javaScriptEnabled = true</span><br><span class="line">          webView.webViewClient = WebViewClient()</span><br><span class="line">          webView.loadUrl(&quot;https://www.baidu.com&quot;)</span><br><span class="line">  </span><br><span class="line">  //        webView.apply &#123;</span><br><span class="line">  //            settings.javaScriptEnabled = true</span><br><span class="line">  //            webViewClient = WebViewClient()</span><br><span class="line">  //            webView.loadUrl(&quot;https://www.baidu.com&quot;)</span><br><span class="line">  //        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure></div></div></span></pre></td></tr></table></figure></li>
<li>
<p>调用 <code>setJavaScriptEnabled()</code>  方法，让 webView 支持 JavaScript 脚本</p>
</li>
<li>
<p>调用 webView 的 <code>setViewClient()</code>  方法，并传入了一个 <code>WebViewClient</code>  的实例，这段代码的作用是，当需要从一个网页跳转到另一个网页时，我们仍然在当前 webView 中显示，而不是打开系统浏览器</p>
</li>
<li>
<p>最后一步调用 webView 的 <code>loadUrl()</code>  方法，并将网址传入，即可展示相应网页的内容</p>
</li>
<li>
<p>最最后，值得注意的是，我们使用了网络功能，而访问网络是需要声明权限的，因此我们还得修改 <code>AndroidManifest.xml</code>  文件，加入权限声明:</p>
<ul>
<li>
<div class="highlight-container" data-rel="Xml"><figure class="iseeu highlight xml"><figcaption data-lang="XML"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>manifest</span> <span class="token attr-name"><span class="token namespace">xmlns:</span>android</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://schemas.android.com/apk/res/android<span class="token punctuation">"</span></span></pre></td></tr><tr><td data-num="2"></td><td><pre>    <span class="token attr-name">package</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>com.example.webviewtest<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="3"></td><td><pre></pre></td></tr><tr><td data-num="4"></td><td><pre>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>uses-permission</span> <span class="token attr-name"><span class="token namespace">android:</span>name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>android.permission.INTERNET<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></pre></td></tr><tr><td data-num="5"></td><td><pre></pre></td></tr><tr><td data-num="6"></td><td><pre>    ...</pre></td></tr><tr><td data-num="7"></td><td><pre></pre></td></tr><tr><td data-num="8"></td><td><pre><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>manifest</span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="9"></td><td><pre><span class="token comment"><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">      </span><br><span class="line"></span><br><span class="line">### 使用HTTP访问网络</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">- **注意**</span><br><span class="line">  - **使用Android虚拟机访问本机服务器时，需要使用本机的ip地址，而不是``127.0.0.1``**</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">#### 使用HttpURLConnection</span><br><span class="line"></span><br><span class="line">- 在过去，Android上发送HTTP请求一般有两种方式：HttpURLConnection和HttpClient。不过由于HttpClient存在API数量过多、扩展困难等缺点，Android团队越来越不建议我们使用这种方式。终于在Android 6.0系统中，HttpClient的功能被完全移除了，标志着此功能被正式弃用</span><br><span class="line"></span><br><span class="line">- HttpURLConnection的用法</span><br><span class="line"></span><br><span class="line">  - 首先需要获取HttpURLConnection的实例，一般只需要创建一个URL对象，并传入目标的网络地址，然后调用一下``openConnection()``方法即可,如下所示：</span><br><span class="line"></span><br><span class="line">    - ```kotlin</span><br><span class="line">      val url = URL(&quot;https://www.baidu.com&quot;)</span><br><span class="line">      val connection = url.openConnection() as HttpURLConnection</span><br></pre></td></tr></table></figure></div></div></span></pre></td></tr></table></figure></li>
</ul>
</li>
<li>
<p>在得到了 HttpURLConnection 的实例之后，我们可以设置一下 HTTP 请求所使用的方法，常用的方法主要有两个： <code>GET</code>  和 <code>POST</code> 。</p>
<ul>
<li>
<p><code>GET</code>  表示希望从服务器那里获取数据</p>
</li>
<li>
<p><code>POST</code>  表示希望提交数据给服务器</p>
</li>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre>connection<span class="token punctuation">.</span>requestMethod <span class="token operator">=</span> <span class="token string-literal singleline"><span class="token string">"GET"</span></span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">51</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>之后再调用 <code>getInputStream()</code>  方法就可以获取到服务器的输入流了，剩下的任务就是对输入流进行读取</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">val</span> input <span class="token operator">=</span> connection<span class="token punctuation">.</span>inputStream</pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">52</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
</ul>
</li>
<li>
<p>而如果想要提交数据给服务器的话，只需要将 http 请求的方法改成 POST，并在获取输入流之前把要提交的数据写出即可</p>
<ul>
<li>
<p>注意：</p>
<ul>
<li>
<p>每条数据都要以键值对的形式存在，数据与数据之间用 <code>&amp;</code>  符号隔开，比如我们想要向服务器提交用户名和密码，就可以这样写：</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre>connection<span class="token punctuation">.</span>requestMethod <span class="token operator">=</span> <span class="token string-literal singleline"><span class="token string">"POST"</span></span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">val</span> output <span class="token operator">=</span> <span class="token function">DataOutputStream</span><span class="token punctuation">(</span>connection<span class="token punctuation">.</span>outputStream<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="3"></td><td><pre>output<span class="token punctuation">.</span><span class="token function">writeBytes</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"username=admin&amp;password=123456"</span></span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">53</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>
<p>具体用法：</p>
<ul>
<li>
<p>创建一个 <code>OkHttpClient</code>  的实例</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">val</span> client <span class="token operator">=</span> <span class="token function">OkHttpClient</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">54</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>当然上述代码只是创建了一个空的 <code>Request</code>  对象，并没有什么实际用处，我们可以在最终的 <code>build()</code>  方法之前连缀很多其他方法来丰富这个 <code>Request</code>  对象，比如可以通过 <code>url()</code>  方法来设置目标的网络地址</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">val</span> request <span class="token operator">=</span> Request<span class="token punctuation">.</span><span class="token function">Builder</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="2"></td><td><pre>	<span class="token punctuation">.</span><span class="token function">url</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"https://www.baidu.com"</span></span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="3"></td><td><pre>	<span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">55</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p><code>Response</code>  对象就是服务器返回的数据了，我们可以使用如下写法来得到返回的具体内容:</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">val</span> responseData <span class="token operator">=</span> response<span class="token punctuation">.</span>body<span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">string</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">56</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>然后在 <code>Request.Builder</code>  中调用一下 <code>post()</code>  方法，并将 <code>RequestBody</code>  对象传入:</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">val</span> request <span class="token operator">=</span> Request<span class="token punctuation">.</span><span class="token function">Builder</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="2"></td><td><pre>	<span class="token punctuation">.</span><span class="token function">url</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"https://www.baidu.com"</span></span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="3"></td><td><pre>	<span class="token punctuation">.</span><span class="token function">post</span><span class="token punctuation">(</span>requestBody<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="4"></td><td><pre>	<span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="5"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">57</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">package</span> cn<span class="token punctuation">.</span>wenhe9<span class="token punctuation">.</span>testmenu</pre></td></tr><tr><td data-num="2"></td><td><pre></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token keyword">import</span> androidx<span class="token punctuation">.</span>appcompat<span class="token punctuation">.</span>app<span class="token punctuation">.</span>AppCompatActivity</pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token keyword">import</span> android<span class="token punctuation">.</span>os<span class="token punctuation">.</span>Bundle</pre></td></tr><tr><td data-num="5"></td><td><pre><span class="token keyword">import</span> cn<span class="token punctuation">.</span>wenhe9<span class="token punctuation">.</span>testmenu<span class="token punctuation">.</span>databinding<span class="token punctuation">.</span>ActivityTestOkHttpClientBinding</pre></td></tr><tr><td data-num="6"></td><td><pre><span class="token keyword">import</span> okhttp3<span class="token punctuation">.</span>FormBody</pre></td></tr><tr><td data-num="7"></td><td><pre><span class="token keyword">import</span> okhttp3<span class="token punctuation">.</span>OkHttpClient</pre></td></tr><tr><td data-num="8"></td><td><pre><span class="token keyword">import</span> okhttp3<span class="token punctuation">.</span>Request</pre></td></tr><tr><td data-num="9"></td><td><pre><span class="token keyword">import</span> kotlin<span class="token punctuation">.</span>concurrent<span class="token punctuation">.</span>thread</pre></td></tr><tr><td data-num="10"></td><td><pre></pre></td></tr><tr><td data-num="11"></td><td><pre><span class="token keyword">class</span> TestOkHttpClient <span class="token operator">:</span> <span class="token function">AppCompatActivity</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="12"></td><td><pre></pre></td></tr><tr><td data-num="13"></td><td><pre>    <span class="token keyword">private</span> <span class="token keyword">lateinit</span> <span class="token keyword">var</span> binding <span class="token operator">:</span> ActivityTestOkHttpClientBinding</pre></td></tr><tr><td data-num="14"></td><td><pre></pre></td></tr><tr><td data-num="15"></td><td><pre>    <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">onCreate</span><span class="token punctuation">(</span>savedInstanceState<span class="token operator">:</span> Bundle<span class="token operator">?</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="16"></td><td><pre>        <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">onCreate</span><span class="token punctuation">(</span>savedInstanceState<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="17"></td><td><pre>        binding <span class="token operator">=</span> ActivityTestOkHttpClientBinding<span class="token punctuation">.</span><span class="token function">inflate</span><span class="token punctuation">(</span>layoutInflater<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="18"></td><td><pre>        <span class="token function">setContentView</span><span class="token punctuation">(</span>binding<span class="token punctuation">.</span>root<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="19"></td><td><pre></pre></td></tr><tr><td data-num="20"></td><td><pre>        binding<span class="token punctuation">.</span>sendRequestBtn<span class="token punctuation">.</span><span class="token function">setOnClickListener</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="21"></td><td><pre>            thread <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="22"></td><td><pre>                <span class="token keyword">val</span> client <span class="token operator">=</span> <span class="token function">OkHttpClient</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="23"></td><td><pre></pre></td></tr><tr><td data-num="24"></td><td><pre>                <span class="token keyword">val</span> body <span class="token operator">=</span> FormBody<span class="token punctuation">.</span><span class="token function">Builder</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="25"></td><td><pre>                    <span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"username"</span></span><span class="token punctuation">,</span> <span class="token string-literal singleline"><span class="token string">"admin"</span></span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="26"></td><td><pre>                    <span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"password"</span></span><span class="token punctuation">,</span> <span class="token string-literal singleline"><span class="token string">"123456"</span></span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="27"></td><td><pre>                    <span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="28"></td><td><pre></pre></td></tr><tr><td data-num="29"></td><td><pre>                <span class="token keyword">val</span> request <span class="token operator">=</span> Request<span class="token punctuation">.</span><span class="token function">Builder</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="30"></td><td><pre>                    <span class="token punctuation">.</span><span class="token function">url</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"https://www.baidu.com"</span></span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="31"></td><td><pre>                    <span class="token punctuation">.</span><span class="token function">post</span><span class="token punctuation">(</span>body<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="32"></td><td><pre>                    <span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="33"></td><td><pre></pre></td></tr><tr><td data-num="34"></td><td><pre>                <span class="token keyword">val</span> response <span class="token operator">=</span> client<span class="token punctuation">.</span><span class="token function">newCall</span><span class="token punctuation">(</span>request<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">execute</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="35"></td><td><pre></pre></td></tr><tr><td data-num="36"></td><td><pre>                </pre></td></tr><tr><td data-num="37"></td><td><pre>                <span class="token function">updateUI</span><span class="token punctuation">(</span>response<span class="token punctuation">.</span>body<span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">string</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="38"></td><td><pre>            <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="39"></td><td><pre>        <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="40"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="41"></td><td><pre></pre></td></tr><tr><td data-num="42"></td><td><pre>    <span class="token keyword">private</span> <span class="token keyword">fun</span> <span class="token function">updateUI</span><span class="token punctuation">(</span><span class="token keyword">data</span> <span class="token operator">:</span> String<span class="token operator">?</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="43"></td><td><pre>        runOnUiThread <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="44"></td><td><pre>            binding<span class="token punctuation">.</span>responseText<span class="token punctuation">.</span>text <span class="token operator">=</span> <span class="token keyword">data</span></pre></td></tr><tr><td data-num="45"></td><td><pre>        <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="46"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="47"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="48"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">58</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>下面就来仔细看下 <code>parseXMLWithPull()</code>  方法中的代码吧。这里首先要创建一个 X <code>mlPullParserFactory</code>  的实例，并借助这个实例得到 <code>XmlPullParser</code>  对象，然后调用 <code>XmlPullParser</code>  的 <code>setInput()</code>  方法将服务器返回的 XML 数据设置进去，之后就可以开始解析了。解析的过程也非常简单，通过 <code>getEventType()</code>  可以得到当前的解析事件，然后在一个 while 循环中不断地进行解析，如果当前的解析事件不等于 <code>XmlPullParser.END_DOCUMENT</code> ，说明解析工作还没完成，调用 <code>next()</code>  方法后可以获取下一个解析事件。</p>
</li>
<li>
<p>在 while 循环中，我们通过 <code>getName()</code>  方法得到了当前节点的名字。如果发现节点名等于 id、name 或 version，就调用 <code>nextText()</code>  方法来获取节点内具体的内容，每当解析完一个 app 节点，就将获取到的内容打印出来。</p>
</li>
<li>
<p>不过在程序运行之前还得再进行一项额外的配置。从 Android 9.0 系统开始，应用程序默认只允许使用 HTTPS 类型的网络请求，HTTP 类型的网络请求因为有安全隐患默认不再被支持</p>
</li>
<li>
<p>那么为了能让程序使用 HTTP，我们还要进行如下配置才可以。右击 res 目录→New→Directory，创建一个 xml 目录，接着右击 xml 目录→New→File，创建一个 network_config.xml 文件。然后修改 network_config.xml 文件中的内容，如下所示：</p>
<ul>
<li>
<div class="highlight-container" data-rel="Xml"><figure class="iseeu highlight xml"><figcaption data-lang="XML"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token prolog">&lt;?xml version="1.0" encoding="utf-8"?></span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>network-security-config</span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="3"></td><td><pre>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>base-config</span> <span class="token attr-name">cleartextTrafficPermitted</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="4"></td><td><pre>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>trust-anchors</span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="5"></td><td><pre>            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>certificates</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>system<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></pre></td></tr><tr><td data-num="6"></td><td><pre>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>trust-anchors</span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="7"></td><td><pre>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>base-config</span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="8"></td><td><pre><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>network-security-config</span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="9"></td><td><pre><span class="token comment"><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">  - 这段配置文件的意思就是允许我们以明文的方式在网络上传输数据，而HTTP使用的就是明文传输方式。</span><br><span class="line"></span><br><span class="line">- 接下来修改AndroidManifest.xml中的代码来启用我们刚才创建的配置文件：</span><br><span class="line"></span><br><span class="line">  - ```xml</span><br><span class="line">    &lt;manifest xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;</span><br><span class="line">        package=&quot;com.example.networktest&quot;&gt;</span><br><span class="line">        ...</span><br><span class="line">        &lt;application</span><br><span class="line">            android:allowBackup=&quot;true&quot;</span><br><span class="line">            android:icon=&quot;@mipmap/ic_launcher&quot;</span><br><span class="line">            android:label=&quot;@string/app_name&quot;</span><br><span class="line">            android:roundIcon=&quot;@mipmap/ic_launcher_round&quot;</span><br><span class="line">            android:supportsRtl=&quot;true&quot;</span><br><span class="line">            android:theme=&quot;@style/AppTheme&quot;</span><br><span class="line">            android:networkSecurityConfig=&quot;@xml/network_config&quot;&gt;</span><br><span class="line">            ...</span><br><span class="line">        &lt;/application&gt;</span><br><span class="line">    &lt;/manifest&gt;</span><br></pre></td></tr></table></figure></div></div></span></pre></td></tr></table></figure></li>
</ul>
</li>
</ul>
<h4 id="sax解析方式"><a class="markdownIt-Anchor" href="#sax解析方式">#</a> SAX 解析方式</h4>
<ul>
<li>
<p>yaoshiyongSAX 解析，通常情况下，我们会新建一个类继承自 <code>DefaultHandler</code> ，并重写父类的 5 个方法：</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">package</span> cn<span class="token punctuation">.</span>wenhe9<span class="token punctuation">.</span>testmenu</pre></td></tr><tr><td data-num="2"></td><td><pre></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token keyword">import</span> org<span class="token punctuation">.</span>xml<span class="token punctuation">.</span>sax<span class="token punctuation">.</span>Attributes</pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token keyword">import</span> org<span class="token punctuation">.</span>xml<span class="token punctuation">.</span>sax<span class="token punctuation">.</span>helpers<span class="token punctuation">.</span>DefaultHandler</pre></td></tr><tr><td data-num="5"></td><td><pre></pre></td></tr><tr><td data-num="6"></td><td><pre><span class="token comment">/**</pre></td></tr><tr><td data-num="7"></td><td><pre> *@author DuJinliang</pre></td></tr><tr><td data-num="8"></td><td><pre> *2021/9/28</pre></td></tr><tr><td data-num="9"></td><td><pre> */</span></pre></td></tr><tr><td data-num="10"></td><td><pre><span class="token keyword">class</span> MyHandler <span class="token operator">:</span> <span class="token function">DefaultHandler</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="11"></td><td><pre>    <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">startDocument</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="12"></td><td><pre>        <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">startDocument</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="13"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="14"></td><td><pre></pre></td></tr><tr><td data-num="15"></td><td><pre>    <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">startElement</span><span class="token punctuation">(</span>uri<span class="token operator">:</span> String<span class="token operator">?</span><span class="token punctuation">,</span> localName<span class="token operator">:</span> String<span class="token operator">?</span><span class="token punctuation">,</span> qName<span class="token operator">:</span> String<span class="token operator">?</span><span class="token punctuation">,</span> attributes<span class="token operator">:</span> Attributes<span class="token operator">?</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="16"></td><td><pre>        <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">startElement</span><span class="token punctuation">(</span>uri<span class="token punctuation">,</span> localName<span class="token punctuation">,</span> qName<span class="token punctuation">,</span> attributes<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="17"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="18"></td><td><pre></pre></td></tr><tr><td data-num="19"></td><td><pre>    <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">characters</span><span class="token punctuation">(</span>ch<span class="token operator">:</span> CharArray<span class="token operator">?</span><span class="token punctuation">,</span> start<span class="token operator">:</span> Int<span class="token punctuation">,</span> length<span class="token operator">:</span> Int<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="20"></td><td><pre>        <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">characters</span><span class="token punctuation">(</span>ch<span class="token punctuation">,</span> start<span class="token punctuation">,</span> length<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="21"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="22"></td><td><pre></pre></td></tr><tr><td data-num="23"></td><td><pre>    <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">endElement</span><span class="token punctuation">(</span>uri<span class="token operator">:</span> String<span class="token operator">?</span><span class="token punctuation">,</span> localName<span class="token operator">:</span> String<span class="token operator">?</span><span class="token punctuation">,</span> qName<span class="token operator">:</span> String<span class="token operator">?</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="24"></td><td><pre>        <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">endElement</span><span class="token punctuation">(</span>uri<span class="token punctuation">,</span> localName<span class="token punctuation">,</span> qName<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="25"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="26"></td><td><pre></pre></td></tr><tr><td data-num="27"></td><td><pre>    <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">endDocument</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="28"></td><td><pre>        <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">endDocument</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="29"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="30"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="31"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">60</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
<li>
<p>注意，因为内容中的回车和换行符也会被解析，所以需要在最后使用 <code>trim()</code>  方法</p>
</li>
</ul>
</li>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">private</span> <span class="token keyword">fun</span> <span class="token function">parseXMLWithSAX</span><span class="token punctuation">(</span>xmlData <span class="token operator">:</span> String<span class="token punctuation">)</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre>        <span class="token keyword">try</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="3"></td><td><pre>            <span class="token keyword">val</span> factory <span class="token operator">=</span> SAXParserFactory<span class="token punctuation">.</span><span class="token function">newInstance</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="4"></td><td><pre></pre></td></tr><tr><td data-num="5"></td><td><pre>            <span class="token keyword">val</span> xmlReader <span class="token operator">=</span> factory<span class="token punctuation">.</span><span class="token function">newSAXParser</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>xmlReader</pre></td></tr><tr><td data-num="6"></td><td><pre></pre></td></tr><tr><td data-num="7"></td><td><pre>            <span class="token keyword">val</span> handler <span class="token operator">=</span> <span class="token function">ContentHandler</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="8"></td><td><pre></pre></td></tr><tr><td data-num="9"></td><td><pre>            xmlReader<span class="token punctuation">.</span>contentHandler <span class="token operator">=</span> handler</pre></td></tr><tr><td data-num="10"></td><td><pre></pre></td></tr><tr><td data-num="11"></td><td><pre>            xmlReader<span class="token punctuation">.</span><span class="token function">parse</span><span class="token punctuation">(</span><span class="token function">InputSource</span><span class="token punctuation">(</span><span class="token function">StringReader</span><span class="token punctuation">(</span>xmlData<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="12"></td><td><pre>        <span class="token punctuation">&#125;</span><span class="token keyword">catch</span> <span class="token punctuation">(</span>e <span class="token operator">:</span> Exception<span class="token punctuation">)</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="13"></td><td><pre>            e<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="14"></td><td><pre>        <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="15"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="16"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">61</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr><tr><td data-num="17"></td><td><pre></pre></td></tr><tr><td data-num="18"></td><td><pre><span class="token operator">-</span> 由于我们在服务器中定义的是一个JSON数组，因此这里首先将服务器返回的数据传入一个JSONArray对象中。然后循环遍历这个JSONArray，从中取出的每一个元素都是一个JSONObject对象，每个JSONObject对象中又会包含id、name和version这些数据。接下来只需要调用<span class="token function">getString</span><span class="token punctuation">(</span><span class="token punctuation">)</span>方法将这些数据取出，并打印出来即可</pre></td></tr></table></figure></div></li>
</ul>
<h4 id="使用gson"><a class="markdownIt-Anchor" href="#使用gson">#</a> 使用 GSON</h4>
<ul>
<li>
<p>GSON 可以将一段 JSON 格式的字符串自动映射成一个对象，从而不需要我们再手动编写代码进行解析</p>
</li>
<li>
<p>比如这样一段 JSON 格式的数据</p>
<ul>
<li>
<div class="highlight-container" data-rel="Json"><figure class="iseeu highlight json"><figcaption data-lang="JSON"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token punctuation">&#123;</span><span class="token string">"username” : "</span>马尔扎哈<span class="token string">", "</span>password<span class="token string">" : "</span><span class="token number">12346</span>"<span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="2"></td><td><pre>&lt;!--code￼<span class="token number">62</span>--></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>如果需要解析的是一段 JSON 数组，会稍微麻烦一点，比如如下格式：</p>
<ul>
<li>
<div class="highlight-container" data-rel="Json"><figure class="iseeu highlight json"><figcaption data-lang="JSON"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token punctuation">[</span><span class="token punctuation">&#123;</span><span class="token string">"username” : "</span>马尔扎哈<span class="token string">", "</span>password<span class="token string">" : "</span><span class="token number">12346</span><span class="token string">"&#125;, &#123;"</span>username” <span class="token operator">:</span> <span class="token string">"马尔扎哈"</span><span class="token punctuation">,</span> <span class="token property">"password"</span> <span class="token operator">:</span> <span class="token string">"12346"</span><span class="token punctuation">&#125;</span><span class="token punctuation">,</span> <span class="token punctuation">&#123;</span><span class="token string">"username” : "</span>马尔扎哈<span class="token string">", "</span>password<span class="token string">" : "</span><span class="token number">12346</span>"<span class="token punctuation">&#125;</span><span class="token punctuation">]</span></pre></td></tr><tr><td data-num="2"></td><td><pre>&lt;!--code￼<span class="token number">63</span>--></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">private</span> <span class="token keyword">fun</span> <span class="token function">parseJSONWithGson</span><span class="token punctuation">(</span>jsonData <span class="token operator">:</span> String<span class="token operator">?</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre>        <span class="token keyword">try</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="3"></td><td><pre>            <span class="token keyword">val</span> gson <span class="token operator">=</span> <span class="token function">Gson</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="4"></td><td><pre></pre></td></tr><tr><td data-num="5"></td><td><pre>            <span class="token keyword">val</span> typeOf <span class="token operator">=</span> <span class="token keyword">object</span> <span class="token operator">:</span> TypeToken<span class="token operator">&lt;</span>List<span class="token operator">&lt;</span>User<span class="token operator">></span><span class="token operator">></span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span><span class="token punctuation">&#125;</span><span class="token punctuation">.</span>type</pre></td></tr><tr><td data-num="6"></td><td><pre></pre></td></tr><tr><td data-num="7"></td><td><pre>            <span class="token keyword">val</span> people <span class="token operator">=</span> gson<span class="token punctuation">.</span>fromJson<span class="token operator">&lt;</span>List<span class="token operator">&lt;</span>User<span class="token operator">></span><span class="token operator">></span><span class="token punctuation">(</span>jsonData<span class="token punctuation">,</span> typeOf<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="8"></td><td><pre></pre></td></tr><tr><td data-num="9"></td><td><pre>            <span class="token keyword">for</span><span class="token punctuation">(</span>item <span class="token keyword">in</span> <span class="token number">0</span> until  people<span class="token punctuation">.</span>size<span class="token punctuation">)</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="10"></td><td><pre>                Log<span class="token punctuation">.</span><span class="token function">d</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"TestJson"</span></span><span class="token punctuation">,</span> <span class="token string-literal singleline"><span class="token string">"people is </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">$&#123;</span><span class="token expression">people<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>item<span class="token punctuation">)</span></span><span class="token interpolation-punctuation punctuation">&#125;</span></span><span class="token string">"</span></span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="11"></td><td><pre>            <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="12"></td><td><pre>        <span class="token punctuation">&#125;</span><span class="token keyword">catch</span> <span class="token punctuation">(</span>e <span class="token operator">:</span> Exception<span class="token punctuation">)</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="13"></td><td><pre>            e<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="14"></td><td><pre>        <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="15"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="16"></td><td><pre></pre></td></tr><tr><td data-num="17"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">64</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
<li>
<p>以后每当需要发起一条 HTTP 请求的时候，就可以这样写:</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">val</span> address <span class="token operator">=</span> <span class="token string-literal singleline"><span class="token string">"https://www.baidu.com"</span></span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">val</span> response <span class="token operator">=</span> HttpUtil<span class="token punctuation">.</span><span class="token function">sendHttpRequest</span><span class="token punctuation">(</span>address<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">65</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>可有看到，我们在接口中定义了两个方法</p>
<ul>
<li><code>onFinish(response : String)</code>  方法表示当服务器成功相应我们请求的时候调用，其中的参数代表服务器返回的数据</li>
<li><code>onError(e : Exception)</code>  方法表示当进行网络操作出现错误时调用，其中得到参数记录着错误的详细信息</li>
</ul>
</li>
<li>
<p>修改 <code>HttpUtil</code>  中的代码，如下所示:</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">package</span> cn<span class="token punctuation">.</span>wenhe9<span class="token punctuation">.</span>testmenu</pre></td></tr><tr><td data-num="2"></td><td><pre></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token keyword">import</span> java<span class="token punctuation">.</span>io<span class="token punctuation">.</span>BufferedReader</pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token keyword">import</span> java<span class="token punctuation">.</span>io<span class="token punctuation">.</span>InputStreamReader</pre></td></tr><tr><td data-num="5"></td><td><pre><span class="token keyword">import</span> java<span class="token punctuation">.</span>lang<span class="token punctuation">.</span>Exception</pre></td></tr><tr><td data-num="6"></td><td><pre><span class="token keyword">import</span> java<span class="token punctuation">.</span>net<span class="token punctuation">.</span>HttpURLConnection</pre></td></tr><tr><td data-num="7"></td><td><pre><span class="token keyword">import</span> java<span class="token punctuation">.</span>net<span class="token punctuation">.</span>URL</pre></td></tr><tr><td data-num="8"></td><td><pre><span class="token keyword">import</span> kotlin<span class="token punctuation">.</span>concurrent<span class="token punctuation">.</span>thread</pre></td></tr><tr><td data-num="9"></td><td><pre></pre></td></tr><tr><td data-num="10"></td><td><pre><span class="token comment">/**</pre></td></tr><tr><td data-num="11"></td><td><pre> *@author DuJinliang</pre></td></tr><tr><td data-num="12"></td><td><pre> *2021/9/28</pre></td></tr><tr><td data-num="13"></td><td><pre> */</span></pre></td></tr><tr><td data-num="14"></td><td><pre><span class="token keyword">object</span> HttpUtil <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="15"></td><td><pre>    <span class="token keyword">fun</span> <span class="token function">sendHttpRequest</span><span class="token punctuation">(</span>address <span class="token operator">:</span> String<span class="token punctuation">,</span> listener <span class="token operator">:</span> HttpCallbackListener<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="16"></td><td><pre></pre></td></tr><tr><td data-num="17"></td><td><pre>        thread <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="18"></td><td><pre>            <span class="token keyword">var</span> connection <span class="token operator">:</span> HttpURLConnection<span class="token operator">?</span> <span class="token operator">=</span> <span class="token keyword">null</span></pre></td></tr><tr><td data-num="19"></td><td><pre></pre></td></tr><tr><td data-num="20"></td><td><pre>            <span class="token keyword">try</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="21"></td><td><pre>                <span class="token keyword">val</span> response <span class="token operator">=</span> <span class="token function">StringBuilder</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="22"></td><td><pre>                <span class="token keyword">val</span> url <span class="token operator">=</span> <span class="token function">URL</span><span class="token punctuation">(</span>address<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="23"></td><td><pre>                connection <span class="token operator">=</span> url<span class="token punctuation">.</span><span class="token function">openConnection</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="24"></td><td><pre>                        <span class="token keyword">as</span> HttpURLConnection</pre></td></tr><tr><td data-num="25"></td><td><pre>                connection<span class="token punctuation">.</span>connectTimeout <span class="token operator">=</span> <span class="token number">8000</span></pre></td></tr><tr><td data-num="26"></td><td><pre>                connection<span class="token punctuation">.</span>readTimeout <span class="token operator">=</span> <span class="token number">8000</span></pre></td></tr><tr><td data-num="27"></td><td><pre></pre></td></tr><tr><td data-num="28"></td><td><pre>                <span class="token keyword">val</span> input <span class="token operator">=</span> connection<span class="token punctuation">.</span>inputStream</pre></td></tr><tr><td data-num="29"></td><td><pre></pre></td></tr><tr><td data-num="30"></td><td><pre>                <span class="token keyword">val</span> reader <span class="token operator">=</span> <span class="token function">BufferedReader</span><span class="token punctuation">(</span><span class="token function">InputStreamReader</span><span class="token punctuation">(</span>input<span class="token punctuation">)</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="31"></td><td><pre></pre></td></tr><tr><td data-num="32"></td><td><pre>                reader<span class="token punctuation">.</span><span class="token function">use</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="33"></td><td><pre>                    reader<span class="token punctuation">.</span><span class="token function">forEachLine</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="34"></td><td><pre>                        response<span class="token punctuation">.</span><span class="token function">append</span><span class="token punctuation">(</span>it<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="35"></td><td><pre>                    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="36"></td><td><pre>                <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="37"></td><td><pre></pre></td></tr><tr><td data-num="38"></td><td><pre>                listener<span class="token punctuation">.</span><span class="token function">onFinish</span><span class="token punctuation">(</span>response<span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="39"></td><td><pre></pre></td></tr><tr><td data-num="40"></td><td><pre>            <span class="token punctuation">&#125;</span><span class="token keyword">catch</span> <span class="token punctuation">(</span>e <span class="token operator">:</span> Exception<span class="token punctuation">)</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="41"></td><td><pre>                e<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="42"></td><td><pre>                listener<span class="token punctuation">.</span><span class="token function">onError</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="43"></td><td><pre>            <span class="token punctuation">&#125;</span><span class="token keyword">finally</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="44"></td><td><pre>                connection<span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">disconnect</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="45"></td><td><pre>            <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="46"></td><td><pre>        <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="47"></td><td><pre></pre></td></tr><tr><td data-num="48"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="49"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="50"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">66</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>这样当服务器成功响应的时候，我们就可以在 <code>onFinish()</code>  方法里对响应数据进行处理了。类似地，如果出现了异常，就可以在 <code>onError()</code>  方法里对异常情况进行处理。如此一来，我们就巧妙地利用回调机制将响应数据成功返回给调用方了。</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">fun</span> <span class="token function">sendOkHttpRequest</span><span class="token punctuation">(</span>address <span class="token operator">:</span> String<span class="token punctuation">,</span> callback <span class="token operator">:</span> okhttp3<span class="token punctuation">.</span>Callback<span class="token punctuation">)</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre>    <span class="token keyword">val</span> client <span class="token operator">=</span> <span class="token function">OkHttpClient</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="3"></td><td><pre></pre></td></tr><tr><td data-num="4"></td><td><pre>    <span class="token keyword">val</span> request <span class="token operator">=</span> Request<span class="token punctuation">.</span><span class="token function">Builder</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="5"></td><td><pre>    <span class="token punctuation">.</span><span class="token function">url</span><span class="token punctuation">(</span>address<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="6"></td><td><pre>    <span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="7"></td><td><pre></pre></td></tr><tr><td data-num="8"></td><td><pre>    client<span class="token punctuation">.</span><span class="token function">newCall</span><span class="token punctuation">(</span>request<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">enqueue</span><span class="token punctuation">(</span>callback<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="9"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="10"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">67</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>另外，需要注意的是，不管是使用 HttpURLConnection 还是 OkHttp，最终的回调接口都还是在子线程中运行的，因此我们不可以在这里执行任何的 UI 操作，除非借助 <code>runOnUiThread()</code>  方法来进行线程转换。</p>
</li>
</ul>
<h3 id="网络库retrofit"><a class="markdownIt-Anchor" href="#网络库retrofit">#</a> 网络库 Retrofit</h3>
<h4 id="retrofit的基本用法"><a class="markdownIt-Anchor" href="#retrofit的基本用法">#</a> Retrofit 的基本用法</h4>
<ul>
<li>
<p>Retrofit 的基本设计思想</p>
<ul>
<li>同一款应用程序中所发起的网络请求绝大多数指向的是同一个服务器域名，这个很好理解，因为任何公司的产品，客户端和服务器都是配套的，很难想象一个客户端一会去这个服务器获取数据，一会又要去另外一个服务器获取数据吧？</li>
<li>另外，服务器提供的接口通常是可以根据功能来归类的，比如新增用户、修改用户数据、查询用户数据这几个接口就可以归为一类，上架新书、销售图书、查询可供销售图书这几个接口也可以归为一类。将服务器接口合理归类能够让代码结构变得更加合理，从而提高可阅读性和可维护性</li>
<li>最后，开发者肯定更加习惯于 “调用一个借口，获取他的返回值” 这样的编码方式，但当调用的是服务器接口时，却很难想象该如何使用这样的编码方式，其实大多数人并不关心网络的具体通信细节，但是传统网络库的用法却需要编写太多网络相关的代码</li>
<li>而 Retrofit 的用法就是基于以上几点来设计的，首先我们可以配置好一个根路径，然后在指定服务器接口地址时只需要使用相对路径即可，这样就不用每次都指定完整的 URL 地址了</li>
<li>另外，Retrofit 允许我们对服务器接口进行匪类，将功能同属一类的服务器接口定义到同一个接口文件当中，从而让代码结构变得更加合理</li>
<li>最后，我们也完全不用关心网络通信细节，只需要在接口文件中声明一系列的方法和返回值，然后通过注解的方式指定该方法对应哪个服务器接口， 以及需要提供哪些参数，当我们在程序中调用该方法时，并将相应的数据解析成返回值声明的类型，这就使得我们可以使用更加面向对象的思维来进行网络操作</li>
</ul>
</li>
<li>
<p>要想使用 Retrofit，我们需要在项目中添加必要的依赖库</p>
<ul>
<li>
<div class="highlight-container" data-rel="Groovy"><figure class="iseeu highlight groovy"><figcaption data-lang="groovy"></figcaption><table><tr><td data-num="1"></td><td><pre>dependencies <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre>    implementation <span class="token string">'com.squareup.retrofit2:converter-gson:2.9.0'</span></pre></td></tr><tr><td data-num="3"></td><td><pre>    implementation <span class="token string">'com.squareup.retrofit2:retrofit:2.9.0'</span></pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="5"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">68</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>接下来，我们可以根据服务器接口的功能进行归类，创建不同种类的接口文件，并在其中定义对应具体服务器接口的方法，不过由于我们本机的服务器上其实只有一个获取 JSON 数据的接口，因此这里只需要定义一个接口文件，并包含一个方法即可，新建 <code>UserService</code>  接口，代码如下所示：</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">interface</span> UserService <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre>    <span class="token annotation builtin">@GEt</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"get_data.json"</span></span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="3"></td><td><pre>    <span class="token keyword">fun</span> <span class="token function">getUserData</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">:</span> Call<span class="token operator">&lt;</span>List<span class="token operator">&lt;</span>User<span class="token operator">></span><span class="token operator">></span></pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="5"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">69</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
<li>
<p>可以看到，在 “Get App Data” 按钮的点击事件当中，首先使用了 <code>Retrofit.Builder</code>  来构建一个 Retrofit 对象，其中 baseUrl () 方法用于指定所有 Retrofit 请求的根路径， <code>addConverterFactory()</code>  方法用于指定 Retrofit 在解析数据时所使用的转换库，这里指定成 <code>GsonConverterFactory</code> 。注意这两个方法都是必须调用的。</p>
</li>
<li>
<p>有了 Retrofit 对象之后，我们就可以调用它的 create () 方法，并传入具体 Service 接口所对应的 Class 类型，创建一个该接口的动态代理对象。如果你并不熟悉什么是动态代理也没有关系，你只需要知道有了动态代理对象之后，我们就可以随意调用接口中定义的所有方法，而 Retrofit 会自动执行具体的处理就可以了。</p>
</li>
<li>
<p>对应到上述的代码当中，当调用了 AppService 的 getAppData () 方法时，会返回一个 <code>Call&lt;List&lt;App&gt;&gt;</code>  对象，这时我们再调用一下它的 <code>enqueue()</code>  方法，Retrofit 就会根据注解中配置的服务器接口地址去进行网络请求了，服务器响应的数据会回调到 enqueue () 方法中传入的 Callback 实现里面。需要注意的是，当发起请求的时候，Retrofit 会自动在内部开启子线程，当数据回调到 Callback 中之后，Retrofit 又会自动切换回主线程，整个操作过程中我们都不用考虑线程切换问题。在 Callback 的 <code>onResponse()</code>  方法中，调用 <code>response.body()</code>  方法将会得到 Retrofit 解析后的对象，也就是 List<App>类型的数据，最后遍历 List，将其中的数据打印出来即可。</p>
</li>
</ul>
</li>
<li>
<p>需要注意的是，和之前一样，这里也需要对网络安全进行配置才行</p>
</li>
</ul>
<h4 id="处理复杂的接口地址类型"><a class="markdownIt-Anchor" href="#处理复杂的接口地址类型">#</a> 处理复杂的接口地址类型</h4>
<ul>
<li>
<p>大多数情况下，服务器不可能总是给我们提供静态类型的接口，在很多情境下，接口地址中的部分内容可能会是动态变化的，比如如下的接口地址：</p>
<ul>
<li>
<div class="highlight-container" data-rel="Xml"><figure class="iseeu highlight xml"><figcaption data-lang="XML"></figcaption><table><tr><td data-num="1"></td><td><pre>GET http://example.com/<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>page</span><span class="token punctuation">></span></span>/get_data.json</pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token comment"><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">- 这个接口中，``&lt;page&gt;``代表页数，我们传入不同的页数，服务器返回的数据也会不同，这种接口地址对应到Retrofit当中应该怎么写呢？</span><br><span class="line"></span><br><span class="line">  - ```kotlin</span><br><span class="line">    interface EamlpeService&#123;</span><br><span class="line">        @GET(&quot;&#123;page&#125;/get_data.json&quot;)</span><br><span class="line">        fun getData(@Path(&quot;page&quot;) page : Int) : Call&lt;Data&gt;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></div></div></span></pre></td></tr></table></figure></li>
</ul>
</li>
<li>
<p>在 <code>@GET</code>  注解指定的接口地址当中，这里使用了 <code>{page}</code>  的占位符，然后又在 <code>getData()</code>  方法中添加一个 page 参数，并使用 <code>@Path(&quot;page&quot;)</code>  注解来声明这个参数，这样当调用 <code>getData()</code>  方法发起请求时，Retrofit 就会自动将 page 参数的值替换到占位符的位置，从而组成一个合法的请求地址</p>
</li>
<li>
<p>另外，很多服务器接口还有要求我们传入一系列的参数，格式如下：</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre>GET http<span class="token operator">:</span><span class="token operator">/</span><span class="token operator">/</span>example<span class="token punctuation">.</span>com<span class="token operator">/</span>get_data<span class="token punctuation">.</span>json<span class="token operator">?</span>u<span class="token operator">=</span><span class="token operator">&lt;</span>user<span class="token operator">></span>&amp;t<span class="token operator">=</span><span class="token operator">&lt;</span>token<span class="token operator">></span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">71</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>这里在 getData () 方法中添加了 user 和 token 这两个参数，并使用 <code>@Query</code>  注解对它们进行声明。这样当发起网络请求的时候，Retrofit 就会自动按照带参数 GET 请求的格式将这两个参数构建到请求地址当中。</p>
</li>
<li>
<p>HTTP 并不是只有 GET 请求这一种类型，而是有很多种，其中比较常用的有 GET、POST、PUT、PATCH、DELETE 这几种。它们之间的分工也很明确，简单概括的话，GET 请求用于从服务器获取数据，POST 请求用于向服务器提交数据，PUT 和 PATCH 请求用于修改服务器上的数据，DELETE 请求用于删除服务器上的数据。</p>
</li>
<li>
<p>而 Retrofit 对所有常用的 HTTP 请求类型都进行了支持，使用 <code>@GET、@POST、@PUT、@PATCH、@DELETE</code>  注解，就可以让 Retrofit 发出相应类型的请求了。</p>
</li>
<li>
<p>比如服务器提供了如下接口地址：</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre>DELETE http<span class="token operator">:</span><span class="token operator">/</span><span class="token operator">/</span>example<span class="token punctuation">.</span>com<span class="token operator">/</span><span class="token keyword">data</span><span class="token operator">/</span><span class="token operator">&lt;</span>id<span class="token operator">></span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">72</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>这里使用了 <code>@DELETE</code>  注解来发出 DELETE 类型的请求，并使用了 <code>@Path</code>  注解来动态指定 id，这些都很好理解。但是在返回值声明的时候，我们将 Call 的泛型指定成了 ResponseBody，这是什么意思呢？</p>
</li>
<li>
<p>由于 POST、PUT 、PATCH、DELETE 这几种请求类型与 GET 请求不同，它们更多是用于操作服务器上的数据，而不是获取服务器上的数据，所以通常它们对于服务器响应的数据并不关心。这个时候就可以使用 ResponseBody，表示 Retrofit 能够接收任意类型的响应数据，并且不会对响应数据进行解析。</p>
</li>
<li>
<p>那么如果我们需要向服务器提交数据该怎么写呢？比如如下的接口地址：</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre>POST http<span class="token operator">:</span><span class="token operator">/</span><span class="token operator">/</span>example<span class="token punctuation">.</span>com<span class="token operator">/</span><span class="token keyword">data</span><span class="token operator">/</span>create</pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token punctuation">&#123;</span><span class="token string-literal singleline"><span class="token string">"id"</span></span><span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token string-literal singleline"><span class="token string">"content"</span></span><span class="token operator">:</span> <span class="token string-literal singleline"><span class="token string">"The description for this data."</span></span><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">73</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>可以看到，这里我们在 createData () 方法中声明了一个 Data 类型的参数，并给它加上了 <code>@Body</code>  注解。这样当 Retrofit 发出 POST 请求时，就会自动将 Data 对象中的数据转换成 JSON 格式的文本，并放到 HTTP 请求的 body 部分，服务器在收到请求之后只需要从 body 中将这部分数据解析出来即可。这种写法同样也可以用来给 PUT、PATCH、DELETE 类型的请求提交数据。</p>
</li>
<li>
<p>最后，有些服务器接口还可能会要求我们在 HTTP 请求的 header 中指定参数，比如：</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre>GET http<span class="token operator">:</span><span class="token operator">/</span><span class="token operator">/</span>example<span class="token punctuation">.</span>com<span class="token operator">/</span>get_data<span class="token punctuation">.</span>json</pre></td></tr><tr><td data-num="2"></td><td><pre>User<span class="token operator">-</span>Agent<span class="token operator">:</span> okhttp</pre></td></tr><tr><td data-num="3"></td><td><pre>Cache<span class="token operator">-</span>Control<span class="token operator">:</span> max<span class="token operator">-</span>age<span class="token operator">=</span><span class="token number">0</span></pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">74</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>但是这种写法只能进行静态 header 声明，如果想要动态指定 header 的值，则需要使用 <code>@Header</code>  注解，如下所示：</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">interface</span> ExampleService <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre></pre></td></tr><tr><td data-num="3"></td><td><pre>    <span class="token annotation builtin">@GET</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"get_data.json"</span></span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="4"></td><td><pre>    <span class="token keyword">fun</span> <span class="token function">getData</span><span class="token punctuation">(</span><span class="token annotation builtin">@Header</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"User-Agent"</span></span><span class="token punctuation">)</span> userAgent<span class="token operator">:</span> String<span class="token punctuation">,</span></pre></td></tr><tr><td data-num="5"></td><td><pre>        <span class="token annotation builtin">@Header</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"Cache-Control"</span></span><span class="token punctuation">)</span> cacheControl<span class="token operator">:</span> String<span class="token punctuation">)</span><span class="token operator">:</span> Call<span class="token operator">&lt;</span>Data<span class="token operator">></span></pre></td></tr><tr><td data-num="6"></td><td><pre></pre></td></tr><tr><td data-num="7"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="8"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">75</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
</ul>
<h2 id="material-design"><a class="markdownIt-Anchor" href="#material-design">#</a> Material Design</h2>
<ul>
<li>这块笔记算了，网址<a class="link"   target="_blank" rel="noopener" href="https://weread.qq.com/web/reader/73532150723f022f73516a6kc7432af0210c74d97b01b1c" >第一行代码 <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a></li>
</ul>
<h3 id="toolbar"><a class="markdownIt-Anchor" href="#toolbar">#</a> Toolbar</h3>
<ul>
<li>
<p>ActionBar 由于其设计的原因，被限定只能位于 Activity 的顶部，从而不能实现一些 MaterialDesign 的效果，因此官方现在已经不再建议使用 ActionBar 了</p>
</li>
<li>
<p>Toolbar 的强大之处在于，它不仅继承了 ActionBar 的所有功能，而且灵活性很高，可以配合其他控件完成一些 Material Design 的效果</p>
</li>
<li>
<p>首先你要知道，任何一个新建的项目，默认都是会显示 ActionBar 的，这个想必你已经见识过太多次了。那么这个 ActionBar 到底是从哪里来的呢？其实这是根据项目中指定的主题来显示的。打开 AndroidManifest.xml 文件看一下，如下所示：</p>
<ul>
<li>
<div class="highlight-container" data-rel="Xml"><figure class="iseeu highlight xml"><figcaption data-lang="XML"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>application</span></pre></td></tr><tr><td data-num="2"></td><td><pre>    <span class="token attr-name"><span class="token namespace">android:</span>allowBackup</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span></pre></td></tr><tr><td data-num="3"></td><td><pre>    <span class="token attr-name"><span class="token namespace">android:</span>icon</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>@mipmap/ic_launcher<span class="token punctuation">"</span></span></pre></td></tr><tr><td data-num="4"></td><td><pre>    <span class="token attr-name"><span class="token namespace">android:</span>label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>@string/app_name<span class="token punctuation">"</span></span></pre></td></tr><tr><td data-num="5"></td><td><pre>    <span class="token attr-name"><span class="token namespace">android:</span>roundIcon</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>@mipmap/ic_launcher_round<span class="token punctuation">"</span></span></pre></td></tr><tr><td data-num="6"></td><td><pre>    <span class="token attr-name"><span class="token namespace">android:</span>supportsRtl</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span></pre></td></tr><tr><td data-num="7"></td><td><pre>    <span class="token attr-name"><span class="token namespace">android:</span>theme</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>@style/AppTheme<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="8"></td><td><pre>    ...</pre></td></tr><tr><td data-num="9"></td><td><pre><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>application</span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="10"></td><td><pre><span class="token comment"><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">- 可以看到，这里使用android:theme属性指定了一个AppTheme的主题。那么这个AppTheme又是在哪里定义的呢？打开``res/values/styles.xml``文件，代码如下所示：</span><br><span class="line"></span><br><span class="line">  - ```kotlin</span><br><span class="line">    &lt;resources&gt;</span><br><span class="line">    </span><br><span class="line">        &lt;!-- Base application theme. --&gt;</span><br><span class="line">        &lt;style name=&quot;AppTheme&quot; parent=&quot;Theme.AppCompat.Light.DarkActionBar&quot;&gt;</span><br><span class="line">            &lt;!-- Customize your theme here. --&gt;</span><br><span class="line">            &lt;item name=&quot;colorPrimary&quot;&gt;@color/colorPrimary&lt;/item&gt;</span><br><span class="line">            &lt;item name=&quot;colorPrimaryDark&quot;&gt;@color/colorPrimaryDark&lt;/item&gt;</span><br><span class="line">            &lt;item name=&quot;colorAccent&quot;&gt;@color/colorAccent&lt;/item&gt;</span><br><span class="line">        &lt;/style&gt;</span><br><span class="line">    </span><br><span class="line">    &lt;/resources&gt;</span><br></pre></td></tr></table></figure></div></div></span></pre></td></tr></table></figure></li>
</ul>
</li>
<li>
<p>这里定义了一个叫 AppTheme 的主题，然后指定它的 parent 主题是 Theme.AppCompat.Light.DarkActionBar。这个 DarkActionBar 是一个深色的 ActionBar 主题，我们之前所有的项目中自带的 ActionBar 就是因为指定了这个主题才出现的。</p>
</li>
<li>
<p>而现在我们准备使用 Toolbar 来替代 ActionBar，因此需要指定一个不带 ActionBar 的主题，通常有 <code>Theme.AppCompat.NoActionBar </code> 和 <code>Theme.AppCompat.Light.NoActionBar</code>  这两种主题可选。</p>
<ul>
<li>
<p><code>Theme.AppCompat.NoActionBar</code>  表示深色主题，它会将界面的主体颜色设成深色，陪衬颜色设成浅色。</p>
</li>
<li>
<p><code>Theme.AppCompat.Light.NoActionBar</code>  表示浅色主题，它会将界面的主体颜色设成浅色，陪衬颜色设成深色。</p>
</li>
<li>
<div class="highlight-container" data-rel="Xml"><figure class="iseeu highlight xml"><figcaption data-lang="XML"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>resources</span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="2"></td><td><pre></pre></td></tr><tr><td data-num="3"></td><td><pre>    <span class="token comment">&lt;!-- Base application theme. --></span></pre></td></tr><tr><td data-num="4"></td><td><pre>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>style</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>AppTheme<span class="token punctuation">"</span></span> <span class="token attr-name">parent</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Theme.AppCompat.Light.NoActionBar<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="5"></td><td><pre>        <span class="token comment">&lt;!-- Customize your theme here. --></span></pre></td></tr><tr><td data-num="6"></td><td><pre>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>item</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>colorPrimary<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>@color/colorPrimary<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>item</span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="7"></td><td><pre>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>item</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>colorPrimaryDark<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>@color/colorPrimaryDark<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>item</span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="8"></td><td><pre>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>item</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>colorAccent<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>@color/colorAccent<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>item</span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="9"></td><td><pre>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>style</span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="10"></td><td><pre></pre></td></tr><tr><td data-num="11"></td><td><pre><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>resources</span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="12"></td><td><pre><span class="token comment"><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">- 观察一下AppTheme中的属性重写，这里重写了``colorPrimary``、``colorPrimaryDark``和``colorAccent``这3个属性的颜色。那么这3个属性分别代表什么位置的颜色呢？我用语言比较难描述清楚，还是通过一张图来理解一下吧，如图所示</span><br><span class="line"></span><br><span class="line">  - ![image-20210929184212315](http://tuchuang.wenhe9.cn/image-20210929184212315.png)</span><br><span class="line"></span><br><span class="line">- 除了上述3个属性之外，我们还可以通过``textColorPrimary``、``windowBackground``和``navigationBarColor``等属性控制更多位置的颜色。不过唯独``colorAccent``这个属性比较难理解，它不只是用来指定这样一个按钮的颜色，而是更多表达了一种强调的意思，比如一些控件的选中状态也会使用``colorAccent``的颜色。</span><br><span class="line"></span><br><span class="line">- 现在我们已经将ActionBar隐藏起来了，那么接下来看一看如何使用Toolbar来替代ActionBar。修改activity_main.xml中的代码，如下所示：</span><br><span class="line"></span><br><span class="line">  - ```xml</span><br><span class="line">    &lt;FrameLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;</span><br><span class="line">        xmlns:app=&quot;http://schemas.android.com/apk/res-auto&quot;</span><br><span class="line">        android:layout_width=&quot;match_parent&quot;</span><br><span class="line">        android:layout_height=&quot;match_parent&quot;&gt;</span><br><span class="line">    </span><br><span class="line">        &lt;androidx.appcompat.widget.Toolbar</span><br><span class="line">            android:id=&quot;@+id/toolbar&quot;</span><br><span class="line">            android:layout_width=&quot;match_parent&quot;</span><br><span class="line">            android:layout_height=&quot;?attr/actionBarSize&quot;</span><br><span class="line">            android:background=&quot;@color/colorPrimary&quot;</span><br><span class="line">            android:theme=&quot;@style/ThemeOverlay.AppCompat.Dark.ActionBar&quot;</span><br><span class="line">            app:popupTheme=&quot;@style/ThemeOverlay.AppCompat.Light&quot; /&gt;</span><br><span class="line">    </span><br><span class="line">    &lt;/FrameLayout&gt;</span><br></pre></td></tr></table></figure></div></div></span></pre></td></tr></table></figure></li>
</ul>
</li>
<li>
<p>虽然这段代码不长，但是里面着实有不少技术点是需要我们仔细琢磨一下的。首先看一下第 2 行，这里使用 xmlns:app 指定了一个新的命名空间。思考一下，正是由于每个布局文件都会使用 xmlns:android 来指定一个命名空间，我们才能一直使用 android:id、android: layout_width 等写法。这里指定了 xmlns:app，也就是说现在可以使用 app:attribute 这样的写法了。但是为什么这里要指定一个 xmlns:app 的命名空间呢？这是由于许多 Material 属性是在新系统中新增的，老系统中并不存在，那么为了能够兼容老系统，我们就不能使用 android:attribute 这样的写法了，而是应该使用 app:attribute。</p>
</li>
<li>
<p>接下来定义了一个 Toolbar 控件，这个控件是由 appcompat 库提供的。这里我们给 Toolbar 指定了一个 id，将它的宽度设置为 match_parent，高度设置为 actionBar 的高度，背景色设置为 colorPrimary。不过下面的部分就稍微有点难理解了，由于我们刚才在 styles.xml 中将程序的主题指定成了浅色主题，因此 Toolbar 现在也是浅色主题，那么 Toolbar 上面的各种元素就会自动使用深色系，从而和主体颜色区别开。但是之前使用 ActionBar 时文字都是白色的，现在变成黑色的会很难看。那么为了能让 Toolbar 单独使用深色主题，这里我们使用了 android:theme 属性，将 Toolbar 的主题指定成了 ThemeOverlay.AppCompat.Dark.ActionBar。但是这样指定之后又会出现新的问题，如果 Toolbar 中有菜单按钮（我们在 3.2.5 小节中学过），那么弹出的菜单项也会变成深色主题，这样就再次变得十分难看了，于是这里又使用了 app:popupTheme 属性，单独将弹出的菜单项指定成了浅色主题。</p>
</li>
<li>
<p>写完了布局，接下来我们修改 MainActivity，代码如下所示：</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">class</span> MainActivity <span class="token operator">:</span> <span class="token function">AppCompatActivity</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre></pre></td></tr><tr><td data-num="3"></td><td><pre>    <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">onCreate</span><span class="token punctuation">(</span>savedInstanceState<span class="token operator">:</span> Bundle<span class="token operator">?</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="4"></td><td><pre>        <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">onCreate</span><span class="token punctuation">(</span>savedInstanceState<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="5"></td><td><pre>        <span class="token function">setContentView</span><span class="token punctuation">(</span>R<span class="token punctuation">.</span>layout<span class="token punctuation">.</span>activity_main<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="6"></td><td><pre>        <span class="token function">setSupportActionBar</span><span class="token punctuation">(</span>toolbar<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="7"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="8"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="9"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">78</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>这里给 activity 增加了一个 android:label 属性，用于指定在 Toolbar 中显示的文字内容，如果没有指定的话，会默认使用 application 中指定的 label 内容，也就是我们的应用名称。</p>
</li>
<li>
<p>不过只有一个标题的 Toolbar 看起来太单调了，我们还可以再添加一些 action 按钮来让 Toolbar 更加丰富一些。这里我提前准备了几张图片作为按钮的图标，将它们放在了 drawable-xxhdpi 目录下（资源下载方式见前言）。现在右击 res 目录→New→Directory，创建一个 menu 文件夹。然后右击 menu 文件夹→New→Menu resource file，创建一个 toolbar.xml 文件，并编写如下代码：</p>
<ul>
<li>
<div class="highlight-container" data-rel="Xml"><figure class="iseeu highlight xml"><figcaption data-lang="XML"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>menu</span> <span class="token attr-name"><span class="token namespace">xmlns:</span>android</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://schemas.android.com/apk/res/android<span class="token punctuation">"</span></span></pre></td></tr><tr><td data-num="2"></td><td><pre>      <span class="token attr-name"><span class="token namespace">xmlns:</span>app</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://schemas.android.com/apk/res-auto<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="3"></td><td><pre>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>item</span></pre></td></tr><tr><td data-num="4"></td><td><pre>        <span class="token attr-name"><span class="token namespace">android:</span>id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>@+id/backup<span class="token punctuation">"</span></span></pre></td></tr><tr><td data-num="5"></td><td><pre>        <span class="token attr-name"><span class="token namespace">android:</span>icon</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>@drawable/ic_backup<span class="token punctuation">"</span></span></pre></td></tr><tr><td data-num="6"></td><td><pre>        <span class="token attr-name"><span class="token namespace">android:</span>title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Backup<span class="token punctuation">"</span></span></pre></td></tr><tr><td data-num="7"></td><td><pre>        <span class="token attr-name"><span class="token namespace">app:</span>showAsAction</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>always<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></pre></td></tr><tr><td data-num="8"></td><td><pre>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>item</span></pre></td></tr><tr><td data-num="9"></td><td><pre>        <span class="token attr-name"><span class="token namespace">android:</span>id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>@+id/delete<span class="token punctuation">"</span></span></pre></td></tr><tr><td data-num="10"></td><td><pre>        <span class="token attr-name"><span class="token namespace">android:</span>icon</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>@drawable/ic_delete<span class="token punctuation">"</span></span></pre></td></tr><tr><td data-num="11"></td><td><pre>        <span class="token attr-name"><span class="token namespace">android:</span>title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Delete<span class="token punctuation">"</span></span></pre></td></tr><tr><td data-num="12"></td><td><pre>        <span class="token attr-name"><span class="token namespace">app:</span>showAsAction</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>ifRoom<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></pre></td></tr><tr><td data-num="13"></td><td><pre>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>item</span></pre></td></tr><tr><td data-num="14"></td><td><pre>        <span class="token attr-name"><span class="token namespace">android:</span>id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>@+id/settings<span class="token punctuation">"</span></span></pre></td></tr><tr><td data-num="15"></td><td><pre>        <span class="token attr-name"><span class="token namespace">android:</span>icon</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>@drawable/ic_settings<span class="token punctuation">"</span></span></pre></td></tr><tr><td data-num="16"></td><td><pre>        <span class="token attr-name"><span class="token namespace">android:</span>title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Settings<span class="token punctuation">"</span></span></pre></td></tr><tr><td data-num="17"></td><td><pre>        <span class="token attr-name"><span class="token namespace">app:</span>showAsAction</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>never<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></pre></td></tr><tr><td data-num="18"></td><td><pre><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>menu</span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="19"></td><td><pre><span class="token comment"><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">- 可以看到，我们通过&lt;item&gt;标签来定义action按钮，android:id用于指定按钮的id，android:icon用于指定按钮的图标，android:title用于指定按钮的文字。</span><br><span class="line"></span><br><span class="line">- 接着使用app:showAsAction来指定按钮的显示位置，这里之所以再次使用了app命名空间，同样是为了能够兼容低版本的系统。showAsAction主要有以下几种值可选：always表示永远显示在Toolbar中，如果屏幕空间不够则不显示；ifRoom表示屏幕空间足够的情况下显示在Toolbar中，不够的话就显示在菜单当中；never则表示永远显示在菜单当中。注意，Toolbar中的action按钮只会显示图标，菜单中的action按钮只会显示文字。</span><br><span class="line"></span><br><span class="line">  - ```kotlin</span><br><span class="line">    class MainActivity : AppCompatActivity() &#123;</span><br><span class="line">        ...</span><br><span class="line">        override fun onCreateOptionsMenu(menu: Menu?): Boolean &#123;</span><br><span class="line">            menuInflater.inflate(R.menu.toolbar, menu)</span><br><span class="line">            return true</span><br><span class="line">        &#125;</span><br><span class="line">    </span><br><span class="line">        override fun onOptionsItemSelected(item: MenuItem): Boolean &#123;</span><br><span class="line">            when (item.itemId) &#123;</span><br><span class="line">                R.id.backup -&gt; Toast.makeText(this, &quot;You clicked Backup&quot;,</span><br><span class="line">                                   Toast.LENGTH_SHORT).show()</span><br><span class="line">                R.id.delete -&gt; Toast.makeText(this, &quot;You clicked Delete&quot;,</span><br><span class="line">                                   Toast.LENGTH_SHORT).show()</span><br><span class="line">                R.id.settings -&gt; Toast.makeText(this, &quot;You clicked Settings&quot;,</span><br><span class="line">                                     Toast.LENGTH_SHORT).show()</span><br><span class="line">            &#125;</span><br><span class="line">            return true</span><br><span class="line">        &#125;</span><br><span class="line">    </span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></div></div></span></pre></td></tr></table></figure></li>
</ul>
</li>
<li>
<p>非常简单，我们在 onCreateOptionsMenu () 方法中加载了 toolbar.xml 这个菜单文件，然后在 onOptionsItemSelected () 方法中处理各个按钮的点击事件。现在重新运行一下程序，效果如图所示</p>
<ul>
<li><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://tuchuang.wenhe9.cn/image-20210929190303212.png"
                      alt="image-20210929190303212"
                ></li>
</ul>
</li>
<li>
<p>可以看到，Toolbar 上现在显示了两个 action 按钮，这是因为 Backup 按钮指定的显示位置是 always，Delete 按钮指定的显示位置是 ifRoom，而现在屏幕空间很充足，因此两个按钮都会显示在 Toolbar 中。另外一个 Settings 按钮由于指定的显示位置是 never，所以不会显示在 Toolbar 中，点击一下最右边的菜单按钮来展开菜单项，你就能找到 Settings 按钮了。另外，这些 action 按钮都是可以响应点击事件的</p>
</li>
</ul>
<h3 id="drawerlayout"><a class="markdownIt-Anchor" href="#drawerlayout">#</a> DrawerLayout</h3>
<div class="highlight-container" data-rel="Xml"><figure class="iseeu highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">androidx.drawerlayout.widget.DrawerLayout</span></span></span><br><span class="line"><span class="tag">    <span class="attr">xmlns:android</span>=<span class="string">&quot;http://schemas.android.com/apk/res/android&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">xmlns:app</span>=<span class="string">&quot;http://schemas.android.com/apk/res-auto&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:id</span>=<span class="string">&quot;@+id/drawerLayout&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:layout_width</span>=<span class="string">&quot;match_parent&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:layout_height</span>=<span class="string">&quot;match_parent&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">FrameLayout</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_width</span>=<span class="string">&quot;match_parent&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_height</span>=<span class="string">&quot;match_parent&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="tag">&lt;<span class="name">androidx.appcompat.widget.Toolbar</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:id</span>=<span class="string">&quot;@+id/toolbar&quot;</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:layout_width</span>=<span class="string">&quot;match_parent&quot;</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:layout_height</span>=<span class="string">&quot;?attr/actionBarSize&quot;</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:background</span>=<span class="string">&quot;@color/colorPrimary&quot;</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:theme</span>=<span class="string">&quot;@style/ThemeOverlay.AppCompat.Dark.ActionBar&quot;</span></span></span><br><span class="line"><span class="tag">            <span class="attr">app:popupTheme</span>=<span class="string">&quot;@style/ThemeOverlay.AppCompat.Light&quot;</span> /&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;/<span class="name">FrameLayout</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">TextView</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_width</span>=<span class="string">&quot;match_parent&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_height</span>=<span class="string">&quot;match_parent&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_gravity</span>=<span class="string">&quot;start&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:background</span>=<span class="string">&quot;#FFF&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:text</span>=<span class="string">&quot;This is menu&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:textSize</span>=<span class="string">&quot;30sp&quot;</span> /&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">androidx.drawerlayout.widget.DrawerLayout</span>&gt;</span></span><br></pre></td></tr></table></figure></div>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">MainActivity</span> : <span class="type">AppCompatActivity</span>() &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">onCreate</span><span class="params">(savedInstanceState: <span class="type">Bundle</span>?)</span></span> &#123;</span><br><span class="line">        <span class="keyword">super</span>.onCreate(savedInstanceState)</span><br><span class="line">        setContentView(R.layout.activity_main)</span><br><span class="line">        setSupportActionBar(toolbar)</span><br><span class="line">        supportActionBar?.let &#123;</span><br><span class="line">            it.setDisplayHomeAsUpEnabled(<span class="literal">true</span>)</span><br><span class="line">            it.setHomeAsUpIndicator(R.drawable.ic_menu)</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    ...</span><br><span class="line">    <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">onOptionsItemSelected</span><span class="params">(item: <span class="type">MenuItem</span>)</span></span>: <span class="built_in">Boolean</span> &#123;</span><br><span class="line">        <span class="keyword">when</span> (item.itemId) &#123;</span><br><span class="line">            android.R.id.home -&gt; drawerLayout.openDrawer(GravityCompat.START)</span><br><span class="line">            ...</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div>
<h3 id="navigationview"><a class="markdownIt-Anchor" href="#navigationview">#</a> NavigationView</h3>
<div class="highlight-container" data-rel="Groovy"><figure class="iseeu highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">dependencies &#123;</span><br><span class="line">    ...</span><br><span class="line">    implementation <span class="string">&#x27;com.google.android.material:material:1.1.0&#x27;</span></span><br><span class="line">    implementation <span class="string">&#x27;de.hdodenhof:circleimageview:3.0.1&#x27;</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div>
<div class="highlight-container" data-rel="Xml"><figure class="iseeu highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">menu</span> <span class="attr">xmlns:android</span>=<span class="string">&quot;http://schemas.android.com/apk/res/android&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">group</span> <span class="attr">android:checkableBehavior</span>=<span class="string">&quot;single&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">item</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:id</span>=<span class="string">&quot;@+id/navCall&quot;</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:icon</span>=<span class="string">&quot;@drawable/nav_call&quot;</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:title</span>=<span class="string">&quot;Call&quot;</span> /&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">item</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:id</span>=<span class="string">&quot;@+id/navFriends&quot;</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:icon</span>=<span class="string">&quot;@drawable/nav_friends&quot;</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:title</span>=<span class="string">&quot;Friends&quot;</span> /&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">item</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:id</span>=<span class="string">&quot;@+id/navLocation&quot;</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:icon</span>=<span class="string">&quot;@drawable/nav_location&quot;</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:title</span>=<span class="string">&quot;Location&quot;</span> /&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">item</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:id</span>=<span class="string">&quot;@+id/navMail&quot;</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:icon</span>=<span class="string">&quot;@drawable/nav_mail&quot;</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:title</span>=<span class="string">&quot;Mail&quot;</span> /&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">item</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:id</span>=<span class="string">&quot;@+id/navTask&quot;</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:icon</span>=<span class="string">&quot;@drawable/nav_task&quot;</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:title</span>=<span class="string">&quot;Tasks&quot;</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">group</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">menu</span>&gt;</span></span><br></pre></td></tr></table></figure></div>
<div class="highlight-container" data-rel="Xml"><figure class="iseeu highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">RelativeLayout</span> <span class="attr">xmlns:android</span>=<span class="string">&quot;http://schemas.android.com/apk/res/android&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:layout_width</span>=<span class="string">&quot;match_parent&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:layout_height</span>=<span class="string">&quot;180dp&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:padding</span>=<span class="string">&quot;10dp&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:background</span>=<span class="string">&quot;@color/colorPrimary&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">de.hdodenhof.circleimageview.CircleImageView</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:id</span>=<span class="string">&quot;@+id/iconImage&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_width</span>=<span class="string">&quot;70dp&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_height</span>=<span class="string">&quot;70dp&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:src</span>=<span class="string">&quot;@drawable/nav_icon&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_centerInParent</span>=<span class="string">&quot;true&quot;</span> /&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">TextView</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:id</span>=<span class="string">&quot;@+id/mailText&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_width</span>=<span class="string">&quot;wrap_content&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_height</span>=<span class="string">&quot;wrap_content&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_alignParentBottom</span>=<span class="string">&quot;true&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:text</span>=<span class="string">&quot;tonygreendev@gmail.com&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:textColor</span>=<span class="string">&quot;#FFF&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:textSize</span>=<span class="string">&quot;14sp&quot;</span> /&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">TextView</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:id</span>=<span class="string">&quot;@+id/userText&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_width</span>=<span class="string">&quot;wrap_content&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_height</span>=<span class="string">&quot;wrap_content&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_above</span>=<span class="string">&quot;@id/mailText&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:text</span>=<span class="string">&quot;Tony Green&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:textColor</span>=<span class="string">&quot;#FFF&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:textSize</span>=<span class="string">&quot;14sp&quot;</span> /&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">RelativeLayout</span>&gt;</span></span><br></pre></td></tr></table></figure></div>
<div class="highlight-container" data-rel="Xml"><figure class="iseeu highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">androidx.drawerlayout.widget.DrawerLayout</span></span></span><br><span class="line"><span class="tag">    <span class="attr">xmlns:android</span>=<span class="string">&quot;http://schemas.android.com/apk/res/android&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">xmlns:app</span>=<span class="string">&quot;http://schemas.android.com/apk/res-auto&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:id</span>=<span class="string">&quot;@+id/drawerLayout&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:layout_width</span>=<span class="string">&quot;match_parent&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:layout_height</span>=<span class="string">&quot;match_parent&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">FrameLayout</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_width</span>=<span class="string">&quot;match_parent&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_height</span>=<span class="string">&quot;match_parent&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="tag">&lt;<span class="name">androidx.appcompat.widget.Toolbar</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:id</span>=<span class="string">&quot;@+id/toolbar&quot;</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:layout_width</span>=<span class="string">&quot;match_parent&quot;</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:layout_height</span>=<span class="string">&quot;?attr/actionBarSize&quot;</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:background</span>=<span class="string">&quot;@color/colorPrimary&quot;</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:theme</span>=<span class="string">&quot;@style/ThemeOverlay.AppCompat.Dark.ActionBar&quot;</span></span></span><br><span class="line"><span class="tag">            <span class="attr">app:popupTheme</span>=<span class="string">&quot;@style/ThemeOverlay.AppCompat.Light&quot;</span> /&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;/<span class="name">FrameLayout</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">com.google.android.material.navigation.NavigationView</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:id</span>=<span class="string">&quot;@+id/navView&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_width</span>=<span class="string">&quot;match_parent&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_height</span>=<span class="string">&quot;match_parent&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_gravity</span>=<span class="string">&quot;start&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">app:menu</span>=<span class="string">&quot;@menu/nav_menu&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">app:headerLayout</span>=<span class="string">&quot;@layout/nav_header&quot;</span>/&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">androidx.drawerlayout.widget.DrawerLayout</span>&gt;</span></span><br></pre></td></tr></table></figure></div>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">MainActivity</span> : <span class="type">AppCompatActivity</span>() &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">onCreate</span><span class="params">(savedInstanceState: <span class="type">Bundle</span>?)</span></span> &#123;</span><br><span class="line">        <span class="keyword">super</span>.onCreate(savedInstanceState)</span><br><span class="line">        setContentView(R.layout.activity_main)</span><br><span class="line">        setSupportActionBar(toolbar)</span><br><span class="line">        supportActionBar?.let &#123;</span><br><span class="line">            it.setDisplayHomeAsUpEnabled(<span class="literal">true</span>)</span><br><span class="line">            it.setHomeAsUpIndicator(R.drawable.ic_menu)</span><br><span class="line">        &#125;</span><br><span class="line">        navView.setCheckedItem(R.id.navCall)</span><br><span class="line">        navView.setNavigationItemSelectedListener &#123;</span><br><span class="line">            drawerLayout.closeDrawers()</span><br><span class="line">            <span class="literal">true</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div>
<h3 id="floatingactionbutton"><a class="markdownIt-Anchor" href="#floatingactionbutton">#</a> FloatingActionButton</h3>
<div class="highlight-container" data-rel="Xml"><figure class="iseeu highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">androidx.drawerlayout.widget.DrawerLayout</span></span></span><br><span class="line"><span class="tag">    <span class="attr">xmlns:android</span>=<span class="string">&quot;http://schemas.android.com/apk/res/android&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">xmlns:app</span>=<span class="string">&quot;http://schemas.android.com/apk/res-auto&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:id</span>=<span class="string">&quot;@+id/drawerLayout&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:layout_width</span>=<span class="string">&quot;match_parent&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:layout_height</span>=<span class="string">&quot;match_parent&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">FrameLayout</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_width</span>=<span class="string">&quot;match_parent&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_height</span>=<span class="string">&quot;match_parent&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="tag">&lt;<span class="name">androidx.appcompat.widget.Toolbar</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:id</span>=<span class="string">&quot;@+id/toolbar&quot;</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:layout_width</span>=<span class="string">&quot;match_parent&quot;</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:layout_height</span>=<span class="string">&quot;?attr/actionBarSize&quot;</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:background</span>=<span class="string">&quot;@color/colorPrimary&quot;</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:theme</span>=<span class="string">&quot;@style/ThemeOverlay.AppCompat.Dark.ActionBar&quot;</span></span></span><br><span class="line"><span class="tag">            <span class="attr">app:popupTheme</span>=<span class="string">&quot;@style/ThemeOverlay.AppCompat.Light&quot;</span> /&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="tag">&lt;<span class="name">com.google.android.material.floatingactionbutton.FloatingActionButton</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:id</span>=<span class="string">&quot;@+id/fab&quot;</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:layout_width</span>=<span class="string">&quot;wrap_content&quot;</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:layout_height</span>=<span class="string">&quot;wrap_content&quot;</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:layout_gravity</span>=<span class="string">&quot;bottom|end&quot;</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:layout_margin</span>=<span class="string">&quot;16dp&quot;</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:src</span>=<span class="string">&quot;@drawable/ic_done&quot;</span> /&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;/<span class="name">FrameLayout</span>&gt;</span></span><br><span class="line">    ...</span><br><span class="line"><span class="tag">&lt;/<span class="name">androidx.drawerlayout.widget.DrawerLayout</span>&gt;</span></span><br></pre></td></tr></table></figure></div>
<div class="highlight-container" data-rel="Xml"><figure class="iseeu highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">com.google.android.material.floatingactionbutton.FloatingActionButton</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:id</span>=<span class="string">&quot;@+id/fab&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:layout_width</span>=<span class="string">&quot;wrap_content&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:layout_height</span>=<span class="string">&quot;wrap_content&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:layout_gravity</span>=<span class="string">&quot;bottom|end&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:layout_margin</span>=<span class="string">&quot;16dp&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:src</span>=<span class="string">&quot;@drawable/ic_done&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">app:elevation</span>=<span class="string">&quot;8dp&quot;</span> /&gt;</span></span><br></pre></td></tr></table></figure></div>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">MainActivity</span> : <span class="type">AppCompatActivity</span>() &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">onCreate</span><span class="params">(savedInstanceState: <span class="type">Bundle</span>?)</span></span> &#123;</span><br><span class="line">        <span class="keyword">super</span>.onCreate(savedInstanceState)</span><br><span class="line">        setContentView(R.layout.activity_main)</span><br><span class="line">        ...</span><br><span class="line">        fab.setOnClickListener &#123;</span><br><span class="line">            Toast.makeText(<span class="keyword">this</span>, <span class="string">&quot;FAB clicked&quot;</span>, Toast.LENGTH_SHORT).show()</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div>
<h3 id="snackbar"><a class="markdownIt-Anchor" href="#snackbar">#</a> Snackbar</h3>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">MainActivity</span> : <span class="type">AppCompatActivity</span>() &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">onCreate</span><span class="params">(savedInstanceState: <span class="type">Bundle</span>?)</span></span> &#123;</span><br><span class="line">        <span class="keyword">super</span>.onCreate(savedInstanceState)</span><br><span class="line">        setContentView(R.layout.activity_main)</span><br><span class="line">        ...</span><br><span class="line">        fab.setOnClickListener &#123; view -&gt;</span><br><span class="line">            Snackbar.make(view, <span class="string">&quot;Data deleted&quot;</span>, Snackbar.LENGTH_SHORT)</span><br><span class="line">                .setAction(<span class="string">&quot;Undo&quot;</span>) &#123;</span><br><span class="line">                    Toast.makeText(<span class="keyword">this</span>, <span class="string">&quot;Data restored&quot;</span>, Toast.LENGTH_SHORT).show()</span><br><span class="line">                &#125;</span><br><span class="line">                .show()</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div>
<h3 id="coordinatorlayout"><a class="markdownIt-Anchor" href="#coordinatorlayout">#</a> CoordinatorLayout</h3>
<div class="highlight-container" data-rel="Xml"><figure class="iseeu highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">androidx.drawerlayout.widget.DrawerLayout</span></span></span><br><span class="line"><span class="tag">    <span class="attr">xmlns:android</span>=<span class="string">&quot;http://schemas.android.com/apk/res/android&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">xmlns:app</span>=<span class="string">&quot;http://schemas.android.com/apk/res-auto&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:id</span>=<span class="string">&quot;@+id/drawerLayout&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:layout_width</span>=<span class="string">&quot;match_parent&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:layout_height</span>=<span class="string">&quot;match_parent&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">androidx.coordinatorlayout.widget.CoordinatorLayout</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_width</span>=<span class="string">&quot;match_parent&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_height</span>=<span class="string">&quot;match_parent&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="tag">&lt;<span class="name">androidx.appcompat.widget.Toolbar</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:id</span>=<span class="string">&quot;@+id/toolbar&quot;</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:layout_width</span>=<span class="string">&quot;match_parent&quot;</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:layout_height</span>=<span class="string">&quot;?attr/actionBarSize&quot;</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:background</span>=<span class="string">&quot;@color/colorPrimary&quot;</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:theme</span>=<span class="string">&quot;@style/ThemeOverlay.AppCompat.Dark.ActionBar&quot;</span></span></span><br><span class="line"><span class="tag">            <span class="attr">app:popupTheme</span>=<span class="string">&quot;@style/ThemeOverlay.AppCompat.Light&quot;</span> /&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="tag">&lt;<span class="name">com.google.android.material.floatingactionbutton.FloatingActionButton</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:id</span>=<span class="string">&quot;@+id/fab&quot;</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:layout_width</span>=<span class="string">&quot;wrap_content&quot;</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:layout_height</span>=<span class="string">&quot;wrap_content&quot;</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:layout_gravity</span>=<span class="string">&quot;bottom|end&quot;</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:layout_margin</span>=<span class="string">&quot;16dp&quot;</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:src</span>=<span class="string">&quot;@drawable/ic_done&quot;</span> /&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;/<span class="name">androidx.coordinatorlayout.widget.CoordinatorLayout</span>&gt;</span></span><br><span class="line">    ...</span><br><span class="line"><span class="tag">&lt;/<span class="name">androidx.drawerlayout.widget.DrawerLayout</span>&gt;</span></span><br></pre></td></tr></table></figure></div>
<h3 id="卡片式布局"><a class="markdownIt-Anchor" href="#卡片式布局">#</a> 卡片式布局</h3>
<h4 id="materialcardview"><a class="markdownIt-Anchor" href="#materialcardview">#</a> MaterialCardView</h4>
<div class="highlight-container" data-rel="Xml"><figure class="iseeu highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">com.google.android.material.card.MaterialCardView</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:layout_width</span>=<span class="string">&quot;match_parent&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:layout_height</span>=<span class="string">&quot;wrap_content&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">app:cardCornerRadius</span>=<span class="string">&quot;4dp&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">app:elevation</span>=<span class="string">&quot;5dp&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">TextView</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:id</span>=<span class="string">&quot;@+id/infoText&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_width</span>=<span class="string">&quot;match_parent&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_height</span>=<span class="string">&quot;wrap_content&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">com.google.android.material.card.MaterialCardView</span>&gt;</span></span><br></pre></td></tr></table></figure></div>
<h4 id="appbarlayout"><a class="markdownIt-Anchor" href="#appbarlayout">#</a> AppBarLayout</h4>
<div class="highlight-container" data-rel="Xml"><figure class="iseeu highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">androidx.drawerlayout.widget.DrawerLayout</span></span></span><br><span class="line"><span class="tag">    <span class="attr">xmlns:android</span>=<span class="string">&quot;http://schemas.android.com/apk/res/android&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">xmlns:app</span>=<span class="string">&quot;http://schemas.android.com/apk/res-auto&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:id</span>=<span class="string">&quot;@+id/drawerLayout&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:layout_width</span>=<span class="string">&quot;match_parent&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:layout_height</span>=<span class="string">&quot;match_parent&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">androidx.coordinatorlayout.widget.CoordinatorLayout</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_width</span>=<span class="string">&quot;match_parent&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_height</span>=<span class="string">&quot;match_parent&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="tag">&lt;<span class="name">com.google.android.material.appbar.AppBarLayout</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:layout_width</span>=<span class="string">&quot;match_parent&quot;</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:layout_height</span>=<span class="string">&quot;wrap_content&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line">            <span class="tag">&lt;<span class="name">androidx.appcompat.widget.Toolbar</span></span></span><br><span class="line"><span class="tag">                <span class="attr">android:id</span>=<span class="string">&quot;@+id/toolbar&quot;</span></span></span><br><span class="line"><span class="tag">                <span class="attr">android:layout_width</span>=<span class="string">&quot;match_parent&quot;</span></span></span><br><span class="line"><span class="tag">                <span class="attr">android:layout_height</span>=<span class="string">&quot;?attr/actionBarSize&quot;</span></span></span><br><span class="line"><span class="tag">                <span class="attr">android:background</span>=<span class="string">&quot;@color/colorPrimary&quot;</span></span></span><br><span class="line"><span class="tag">                <span class="attr">android:theme</span>=<span class="string">&quot;@style/ThemeOverlay.AppCompat.Dark.ActionBar&quot;</span></span></span><br><span class="line"><span class="tag">                <span class="attr">app:popupTheme</span>=<span class="string">&quot;@style/ThemeOverlay.AppCompat.Light&quot;</span></span></span><br><span class="line"><span class="tag">                <span class="attr">app:layout_scrollFlags</span>=<span class="string">&quot;scroll|enterAlways|snap&quot;</span> /&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="tag">&lt;/<span class="name">com.google.android.material.appbar.AppBarLayout</span>&gt;</span></span><br><span class="line">        ...</span><br><span class="line">    <span class="tag">&lt;/<span class="name">androidx.coordinatorlayout.widget.CoordinatorLayout</span>&gt;</span></span><br><span class="line">    ...</span><br><span class="line"><span class="tag">&lt;/<span class="name">androidx.drawerlayout.widget.DrawerLayout</span>&gt;</span></span><br></pre></td></tr></table></figure></div>
<h3 id="下拉刷新"><a class="markdownIt-Anchor" href="#下拉刷新">#</a> 下拉刷新</h3>
<h4 id="swiperrefreshlayout"><a class="markdownIt-Anchor" href="#swiperrefreshlayout">#</a> SwiperRefreshLayout</h4>
<div class="highlight-container" data-rel="Groovy"><figure class="iseeu highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">dependencies &#123;</span><br><span class="line">...</span><br><span class="line">implementation?<span class="string">&quot;androidx.swiperefreshlayout:swiperefreshlayout:1.0.0&quot;</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div>
<div class="highlight-container" data-rel="Xml"><figure class="iseeu highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">androidx.drawerlayout.widget.DrawerLayout</span></span></span><br><span class="line"><span class="tag">    <span class="attr">xmlns:android</span>=<span class="string">&quot;http://schemas.android.com/apk/res/android&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">xmlns:app</span>=<span class="string">&quot;http://schemas.android.com/apk/res-auto&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:id</span>=<span class="string">&quot;@+id/drawerLayout&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:layout_width</span>=<span class="string">&quot;match_parent&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:layout_height</span>=<span class="string">&quot;match_parent&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">androidx.coordinatorlayout.widget.CoordinatorLayout</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_width</span>=<span class="string">&quot;match_parent&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_height</span>=<span class="string">&quot;match_parent&quot;</span>&gt;</span></span><br><span class="line">        ...</span><br><span class="line">        <span class="tag">&lt;<span class="name">androidx.swiperefreshlayout.widget.SwipeRefreshLayout</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:id</span>=<span class="string">&quot;@+id/swipeRefresh&quot;</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:layout_width</span>=<span class="string">&quot;match_parent&quot;</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:layout_height</span>=<span class="string">&quot;match_parent&quot;</span></span></span><br><span class="line"><span class="tag">            <span class="attr">app:layout_behavior</span>=<span class="string">&quot;@string/appbar_scrolling_view_behavior&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line">            <span class="tag">&lt;<span class="name">androidx.recyclerview.widget.RecyclerView</span></span></span><br><span class="line"><span class="tag">                <span class="attr">android:id</span>=<span class="string">&quot;@+id/recyclerView&quot;</span></span></span><br><span class="line"><span class="tag">                <span class="attr">android:layout_width</span>=<span class="string">&quot;match_parent&quot;</span></span></span><br><span class="line"><span class="tag">                <span class="attr">android:layout_height</span>=<span class="string">&quot;match_parent&quot;</span></span></span><br><span class="line"><span class="tag">                <span class="attr">app:layout_behavior</span>=<span class="string">&quot;@string/appbar_scrolling_view_behavior&quot;</span> /&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="tag">&lt;/<span class="name">androidx.swiperefreshlayout.widget.SwipeRefreshLayout</span>&gt;</span></span><br><span class="line">        ...</span><br><span class="line">    <span class="tag">&lt;/<span class="name">androidx.coordinatorlayout.widget.CoordinatorLayout</span>&gt;</span></span><br><span class="line">    ...</span><br><span class="line"><span class="tag">&lt;/<span class="name">androidx.drawerlayout.widget.DrawerLayout</span>&gt;</span></span><br></pre></td></tr></table></figure></div>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">MainActivity</span> : <span class="type">AppCompatActivity</span>() &#123;</span><br><span class="line">    ...</span><br><span class="line">    <span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">onCreate</span><span class="params">(savedInstanceState: <span class="type">Bundle</span>?)</span></span> &#123;</span><br><span class="line">        <span class="keyword">super</span>.onCreate(savedInstanceState)</span><br><span class="line">        setContentView(R.layout.activity_main)</span><br><span class="line">        ...</span><br><span class="line">        swipeRefresh.setColorSchemeResources(R.color.colorPrimary)</span><br><span class="line">        swipeRefresh.setOnRefreshListener &#123;</span><br><span class="line">            refreshFruits(adapter)</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="function"><span class="keyword">fun</span> <span class="title">refreshFruits</span><span class="params">(adapter: <span class="type">FruitAdapter</span>)</span></span> &#123;</span><br><span class="line">        thread &#123;</span><br><span class="line">            Thread.sleep(<span class="number">2000</span>)</span><br><span class="line">            runOnUiThread &#123;</span><br><span class="line">                initFruits()</span><br><span class="line">                adapter.notifyDataSetChanged()</span><br><span class="line">                swipeRefresh.isRefreshing = <span class="literal">false</span></span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div>
<h3 id="可折叠式标题栏"><a class="markdownIt-Anchor" href="#可折叠式标题栏">#</a> 可折叠式标题栏</h3>
<h4 id="collapsingtoolbarlayout"><a class="markdownIt-Anchor" href="#collapsingtoolbarlayout">#</a> CollapsingToolbarLayout</h4>
<h4 id="充分利用系统状态栏空间"><a class="markdownIt-Anchor" href="#充分利用系统状态栏空间">#</a> 充分利用系统状态栏空间</h4>
<h2 id="jetpack"><a class="markdownIt-Anchor" href="#jetpack">#</a> Jetpack</h2>
<ul>
<li>Jetpack 是一个开发组件工具集，它的主要目的是帮助我们编写出更加简洁的代码，并简化我们的开发过程。Jetpack 中的组件有一个特点，它们大部分不依赖于任何 Android 系统版本，这意味着这些组件通常是定义在 AndroidX 库当中的，并且拥有非常好的向下兼容性。</li>
</ul>
<h3 id="viewmodel"><a class="markdownIt-Anchor" href="#viewmodel">#</a> ViewModel</h3>
<ul>
<li>
<p>ViewModel 应该可以算是 Jetpack 中最重要的组件之一了。其实 Android 平台上之所以会出现诸如 MVP、MVVM 之类的项目架构，就是因为在传统的开发模式下，Activity 的任务实在是太重了，既要负责逻辑处理，又要控制 UI 展示，甚至还得处理网络回调，等等。在一个小型项目中这样写或许没有什么问题，但是如果在大型项目中仍然使用这种写法的话，那么这个项目将会变得非常臃肿并且难以维护，因为没有任何架构上的划分。</p>
</li>
<li>
<p>而 ViewModel 的一个重要作用就是可以帮助 Activity 分担一部分工作，它是专门用于存放与界面相关的数据的。也就是说，只要是界面上能看得到的数据，它的相关变量都应该存放在 ViewModel 中，而不是 Activity 中，这样可以在一定程度上减少 Activity 中的逻辑。</p>
</li>
<li>
<p>另外，ViewModel 还有一个非常重要的特性。我们都知道，当手机发生横竖屏旋转的时候，Activity 会被重新创建，同时存放在 Activity 中的数据也会丢失。而 ViewModel 的生命周期和 Activity 不同，它可以保证在手机屏幕发生旋转的时候不会被重新创建，只有当 Activity 退出的时候才会跟着 Activity 一起销毁。因此，将与界面相关的变量存放在 ViewModel 当中，这样即使旋转手机屏幕，界面上显示的数据也不会丢失。ViewModel 的生命周期如图所示</p>
<ul>
<li><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://tuchuang.wenhe9.cn/image-20210929195306031.png"
                      alt="image-20210929195306031"
                ></li>
</ul>
</li>
</ul>
<h4 id="viewmodel的基本用法"><a class="markdownIt-Anchor" href="#viewmodel的基本用法">#</a> ViewModel 的基本用法</h4>
<ul>
<li>
<p>由于 Jetpack 中的组件通常是以 AndroidX 库的形式发布的，因此一些常用的 Jetpack 组件会在创建 Android 项目时自动被包含进去。不过如果我们想要使用 ViewModel 组件，还需要在 app/build.gradle 文件中添加如下依赖：</p>
<ul>
<li>
<div class="highlight-container" data-rel="Groovy"><figure class="iseeu highlight groovy"><figcaption data-lang="groovy"></figcaption><table><tr><td data-num="1"></td><td><pre>dependencies <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre>    <span class="token punctuation">...</span></pre></td></tr><tr><td data-num="3"></td><td><pre>    implementation <span class="token interpolation-string"><span class="token string">"androidx.lifecycle:lifecycle-extensions:2.2.0"</span></span></pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="5"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">97</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>在 <code>MainActivity</code>  中的使用</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">package</span> cn<span class="token punctuation">.</span>wenhe9<span class="token punctuation">.</span>testmenu</pre></td></tr><tr><td data-num="2"></td><td><pre></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token keyword">import</span> androidx<span class="token punctuation">.</span>appcompat<span class="token punctuation">.</span>app<span class="token punctuation">.</span>AppCompatActivity</pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token keyword">import</span> android<span class="token punctuation">.</span>os<span class="token punctuation">.</span>Bundle</pre></td></tr><tr><td data-num="5"></td><td><pre><span class="token keyword">import</span> androidx<span class="token punctuation">.</span>lifecycle<span class="token punctuation">.</span>ViewModelProvider</pre></td></tr><tr><td data-num="6"></td><td><pre><span class="token keyword">import</span> cn<span class="token punctuation">.</span>wenhe9<span class="token punctuation">.</span>testmenu<span class="token punctuation">.</span>databinding<span class="token punctuation">.</span>ActivityTestViewModelBinding</pre></td></tr><tr><td data-num="7"></td><td><pre></pre></td></tr><tr><td data-num="8"></td><td><pre><span class="token keyword">class</span> TestViewModel <span class="token operator">:</span> <span class="token function">AppCompatActivity</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="9"></td><td><pre></pre></td></tr><tr><td data-num="10"></td><td><pre>    <span class="token keyword">private</span> <span class="token keyword">lateinit</span> <span class="token keyword">var</span> viewModel<span class="token operator">:</span> MainViewModel</pre></td></tr><tr><td data-num="11"></td><td><pre>    <span class="token keyword">private</span> <span class="token keyword">lateinit</span> <span class="token keyword">var</span> binding <span class="token operator">:</span> ActivityTestViewModelBinding</pre></td></tr><tr><td data-num="12"></td><td><pre></pre></td></tr><tr><td data-num="13"></td><td><pre>    <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">onCreate</span><span class="token punctuation">(</span>savedInstanceState<span class="token operator">:</span> Bundle<span class="token operator">?</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="14"></td><td><pre>        <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">onCreate</span><span class="token punctuation">(</span>savedInstanceState<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="15"></td><td><pre>        binding <span class="token operator">=</span> ActivityTestViewModelBinding<span class="token punctuation">.</span><span class="token function">inflate</span><span class="token punctuation">(</span>layoutInflater<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="16"></td><td><pre>        <span class="token function">setContentView</span><span class="token punctuation">(</span>binding<span class="token punctuation">.</span>root<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="17"></td><td><pre></pre></td></tr><tr><td data-num="18"></td><td><pre>        viewModel <span class="token operator">=</span>  <span class="token function">ViewModelProvider</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>MainViewModel<span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">.</span>java<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="19"></td><td><pre></pre></td></tr><tr><td data-num="20"></td><td><pre>        binding<span class="token punctuation">.</span>testViewModel<span class="token punctuation">.</span><span class="token function">setOnClickListener</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="21"></td><td><pre>            viewModel<span class="token punctuation">.</span>counter<span class="token operator">++</span></pre></td></tr><tr><td data-num="22"></td><td><pre>            <span class="token function">refreshCounter</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="23"></td><td><pre>        <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="24"></td><td><pre></pre></td></tr><tr><td data-num="25"></td><td><pre>        <span class="token function">refreshCounter</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="26"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="27"></td><td><pre></pre></td></tr><tr><td data-num="28"></td><td><pre>    <span class="token keyword">private</span> <span class="token keyword">fun</span> <span class="token function">refreshCounter</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="29"></td><td><pre>        binding<span class="token punctuation">.</span>infoText<span class="token punctuation">.</span>text <span class="token operator">=</span> viewModel<span class="token punctuation">.</span>counter<span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="30"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="31"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="32"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">98</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>之所以这样，是因为 <code>ViewModel</code>  有其独立的生命周期，并且其生命周期要长于 Activity，如果我们在 <code>onCreate()</code>  方法中创建爱你 ViewModel 的实例，那么每次 <code>onCreate()</code>  方法执行的时候，ViewModel 都会创建一个新的实例，这样当手机屏幕发生旋转的时候，就无法保留其中的的数据了</p>
</li>
</ul>
<h4 id="向viewmodel传递参数"><a class="markdownIt-Anchor" href="#向viewmodel传递参数">#</a> 向 ViewModel 传递参数</h4>
<ul>
<li>
<p>上一小节中创建的 MainViewModel 的构造函数中没有任何参数，但是思考一下，如果我们确实需要通过构造函数来传递一些参数，应该怎么办呢？由于所有 ViewModel 的实例都是通过 ViewModelProvider 来获取的，因此我们没有任何地方可以向 ViewModel 的构造函数中传递参数。</p>
</li>
<li>
<p>当然，这个问题也不难解决，只需要借助 ViewModelProvider.Factory 就可以实现了</p>
</li>
<li>
<p>现在的计数器虽然在屏幕旋转的时候不会丢失数据，但是如果退出程序之后再重新打开，那么之前的计数就会被清零了。接下来我们就对这一功能进行升级，保证即使在退出程序后又重新打开的情况下，数据仍然不会丢失。</p>
</li>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">package</span> cn<span class="token punctuation">.</span>wenhe9<span class="token punctuation">.</span>testmenu</pre></td></tr><tr><td data-num="2"></td><td><pre></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token keyword">import</span> androidx<span class="token punctuation">.</span>lifecycle<span class="token punctuation">.</span>ViewModel</pre></td></tr><tr><td data-num="4"></td><td><pre></pre></td></tr><tr><td data-num="5"></td><td><pre><span class="token comment">/**</pre></td></tr><tr><td data-num="6"></td><td><pre> *@author DuJinliang</pre></td></tr><tr><td data-num="7"></td><td><pre> *2021/9/29</pre></td></tr><tr><td data-num="8"></td><td><pre> */</span></pre></td></tr><tr><td data-num="9"></td><td><pre><span class="token keyword">class</span> <span class="token function">MainViewModel</span><span class="token punctuation">(</span>countReserved <span class="token operator">:</span> Int<span class="token punctuation">)</span> <span class="token operator">:</span> <span class="token function">ViewModel</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="10"></td><td><pre>    <span class="token keyword">var</span> counter <span class="token operator">=</span> countReserved</pre></td></tr><tr><td data-num="11"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="12"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">99</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">package</span> cn<span class="token punctuation">.</span>wenhe9<span class="token punctuation">.</span>testmenu</pre></td></tr><tr><td data-num="2"></td><td><pre></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token keyword">import</span> androidx<span class="token punctuation">.</span>lifecycle<span class="token punctuation">.</span>ViewModel</pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token keyword">import</span> androidx<span class="token punctuation">.</span>lifecycle<span class="token punctuation">.</span>ViewModelProvider</pre></td></tr><tr><td data-num="5"></td><td><pre></pre></td></tr><tr><td data-num="6"></td><td><pre><span class="token comment">/**</pre></td></tr><tr><td data-num="7"></td><td><pre> *@author DuJinliang</pre></td></tr><tr><td data-num="8"></td><td><pre> *2021/9/29</pre></td></tr><tr><td data-num="9"></td><td><pre> */</span></pre></td></tr><tr><td data-num="10"></td><td><pre><span class="token keyword">class</span> <span class="token function">MainViewModelFactory</span><span class="token punctuation">(</span><span class="token keyword">private</span> <span class="token keyword">val</span> countReserved <span class="token operator">:</span> Int<span class="token punctuation">)</span> <span class="token operator">:</span> ViewModelProvider<span class="token punctuation">.</span><span class="token function">Factory</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="11"></td><td><pre>    <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token operator">&lt;</span>T <span class="token operator">:</span> ViewModel<span class="token operator">?</span><span class="token operator">></span> <span class="token function">create</span><span class="token punctuation">(</span>modelClass<span class="token operator">:</span> Class<span class="token operator">&lt;</span>T<span class="token operator">></span><span class="token punctuation">)</span><span class="token operator">:</span> T <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="12"></td><td><pre>        <span class="token keyword">return</span> <span class="token function">MainViewModel</span><span class="token punctuation">(</span>countReserved<span class="token punctuation">)</span> <span class="token keyword">as</span> T</pre></td></tr><tr><td data-num="13"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="14"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="15"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">100</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr><tr><td data-num="16"></td><td><pre></pre></td></tr><tr><td data-num="17"></td><td><pre><span class="token operator">-</span> 在<span class="token function">onCreate</span><span class="token punctuation">(</span><span class="token punctuation">)</span>方法中，我们首先获取了SharedPreferences的实例，然后读取之前保存的计数值，如果没有读到的话，就使用<span class="token number">0</span>作为默认值。接下来在ViewModelProvider中，额外传入了一个MainViewModelFactory参数，这里将读取到的计数值传给了MainViewModelFactory的构造函数。注意，这一步是非常重要的，只有用这种写法才能将计数值最终传递给MainViewModel的构造函数。</pre></td></tr></table></figure></div></li>
</ul>
<h3 id="lifecycles"><a class="markdownIt-Anchor" href="#lifecycles">#</a> LifeCycles</h3>
<ul>
<li>
<p>在编写 Android 应用程序的时候，可能会经常遇到需要感知 Activity 生命周期的情况。比如说，某个界面中发起了一条网络请求，但是当请求得到响应的时候，界面或许已经关闭了，这个时候就不应该继续对响应的结果进行处理。因此，我们需要能够时刻感知到 Activity 的生命周期，以便在适当的时候进行相应的逻辑控制。</p>
</li>
<li>
<p>问题在于，在一个 Activity 中去感知它的生命周期非常简单，而如果要在一个非 Activity 的类中去感知 Activity 的生命周期，应该怎么办呢？</p>
</li>
<li>
<p>而 Lifecycles 组件就是为了解决这个问题而出现的，它可以让任何一个类都能轻松感知到 Activity 的生命周期，同时又不需要在 Activity 中编写大量的逻辑处理。</p>
</li>
<li>
<p>那么下面我们就通过具体的例子来学习 Lifecycles 组件的用法。新建一个 MyObserver 类，并让它实现 LifecycleObserver 接口，代码如下所示：</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">class</span> MyObserver <span class="token operator">:</span> LifecycleObserver <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">101</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>可以看到，我们在方法上使用了 @OnLifecycleEvent 注解，并传入了一种生命周期事件。生命周期事件的类型一共有 7 种： <code>ON_CREATE</code> 、 <code>ON_START</code> 、 <code>ON_RESUME</code> 、 <code>ON_PAUSE</code> 、 <code>ON_STOP</code>  和 <code>ON_DESTROY</code>  分别匹配 Activity 中相应的生命周期回调；另外还有一种 <code>ON_ANY</code>  类型，表示可以匹配 Activity 的任何生命周期回调。</p>
</li>
<li>
<p>因此，上述代码中的 activityStart () 和 activityStop () 方法就应该分别在 Activity 的 onStart () 和 onStop () 触发的时候执行。</p>
</li>
<li>
<p>但是代码写到这里还是无法正常工作的，因为当 Activity 的生命周期发生变化的时候并没有人去通知 MyObserver，而我们又不想像刚才一样在 Activity 中去一个个手动通知。</p>
</li>
<li>
<p>这个时候就得借助 <code>LifecycleOwner</code>  这个好帮手了，它可以使用如下的语法结构让 MyObserver 得到通知：</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre>lifecyclerOwner<span class="token punctuation">.</span>lifecycle<span class="token punctuation">.</span><span class="token function">addObserver</span><span class="token punctuation">(</span><span class="token function">MyObserver</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">102</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>这些就是 Lifecycles 组件最常见的用法了。不过目前 MyObserver 虽然能够感知到 Activity 的生命周期发生了变化，却没有办法主动获知当前的生命周期状态。要解决这个问题也不难，只需要在 MyObserver 的构造函数中将 Lifecycle 对象传进来即可，如下所示</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">class</span> <span class="token function">MyObserver</span><span class="token punctuation">(</span><span class="token keyword">val</span> lifecycle<span class="token operator">:</span> Lifecycle<span class="token punctuation">)</span> <span class="token operator">:</span> LifecycleObserver <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre>    <span class="token operator">..</span><span class="token punctuation">.</span></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">103</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>这里我们将 counter 变量修改成了一个 <code>MutableLiveData</code>  对象，并指定它的泛型为 Int，表示它包含的是整型数据。 <code>MutableLiveData</code>  是一种可变的 LiveData，它的用法很简单，主要有 3 种读写数据的方法，分别是 <code>getValue()</code> 、 <code>setValue()</code>  和 <code>postValue()</code>  方法。</p>
<ul>
<li>``getValue ()` 方法用于获取 LiveData 中包含的数据；</li>
<li><code>setValue()</code>  方法用于给 LiveData 设置数据，但是只能在主线程中调用；</li>
<li><code>postValue()</code>  方法用于在非主线程中给 LiveData 设置数据。</li>
<li>而上述代码其实就是调用 <code>getValue()</code>  和 <code>setValue()</code>  方法对应的语法糖写法。</li>
</ul>
</li>
<li>
<p>可以看到，这里在 init 结构体中给 counter 设置数据，这样之前保存的计数值就可以在初始化的时候得到恢复。接下来我们新增了 plusOne () 和 clear () 这两个方法，分别用于给计数加 1 以及将计数清零。plusOne () 方法中的逻辑是先获取 counter 中包含的数据，然后给它加 1，再重新设置到 counter 当中。注意调用 LiveData 的 getValue () 方法所获得的数据是可能为空的，因此这里使用了一个？: 操作符，当获取到的数据为空时，就用 0 来作为默认计数。</p>
</li>
<li>
<p>这样我们就借助 LiveData 将 MainViewModel 的写法改造完了，接下来开始改造 MainActivity，代码如下所示：</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">package</span> cn<span class="token punctuation">.</span>wenhe9<span class="token punctuation">.</span>testmenu</pre></td></tr><tr><td data-num="2"></td><td><pre></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token keyword">import</span> android<span class="token punctuation">.</span>content<span class="token punctuation">.</span>Context</pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token keyword">import</span> android<span class="token punctuation">.</span>content<span class="token punctuation">.</span>SharedPreferences</pre></td></tr><tr><td data-num="5"></td><td><pre><span class="token keyword">import</span> androidx<span class="token punctuation">.</span>appcompat<span class="token punctuation">.</span>app<span class="token punctuation">.</span>AppCompatActivity</pre></td></tr><tr><td data-num="6"></td><td><pre><span class="token keyword">import</span> android<span class="token punctuation">.</span>os<span class="token punctuation">.</span>Bundle</pre></td></tr><tr><td data-num="7"></td><td><pre><span class="token keyword">import</span> androidx<span class="token punctuation">.</span>core<span class="token punctuation">.</span>content<span class="token punctuation">.</span>edit</pre></td></tr><tr><td data-num="8"></td><td><pre><span class="token keyword">import</span> androidx<span class="token punctuation">.</span>lifecycle<span class="token punctuation">.</span>Observer</pre></td></tr><tr><td data-num="9"></td><td><pre><span class="token keyword">import</span> androidx<span class="token punctuation">.</span>lifecycle<span class="token punctuation">.</span>ViewModelProvider</pre></td></tr><tr><td data-num="10"></td><td><pre><span class="token keyword">import</span> cn<span class="token punctuation">.</span>wenhe9<span class="token punctuation">.</span>testmenu<span class="token punctuation">.</span>databinding<span class="token punctuation">.</span>ActivityTestViewModelBinding</pre></td></tr><tr><td data-num="11"></td><td><pre></pre></td></tr><tr><td data-num="12"></td><td><pre><span class="token keyword">class</span> TestViewModel <span class="token operator">:</span> <span class="token function">AppCompatActivity</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="13"></td><td><pre></pre></td></tr><tr><td data-num="14"></td><td><pre>    <span class="token keyword">private</span> <span class="token keyword">lateinit</span> <span class="token keyword">var</span> viewModel<span class="token operator">:</span> MainViewModel</pre></td></tr><tr><td data-num="15"></td><td><pre>    <span class="token keyword">private</span> <span class="token keyword">lateinit</span> <span class="token keyword">var</span> binding <span class="token operator">:</span> ActivityTestViewModelBinding</pre></td></tr><tr><td data-num="16"></td><td><pre>    <span class="token keyword">private</span> <span class="token keyword">lateinit</span> <span class="token keyword">var</span> sp <span class="token operator">:</span> SharedPreferences</pre></td></tr><tr><td data-num="17"></td><td><pre></pre></td></tr><tr><td data-num="18"></td><td><pre>    <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">onCreate</span><span class="token punctuation">(</span>savedInstanceState<span class="token operator">:</span> Bundle<span class="token operator">?</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="19"></td><td><pre>        <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">onCreate</span><span class="token punctuation">(</span>savedInstanceState<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="20"></td><td><pre>        binding <span class="token operator">=</span> ActivityTestViewModelBinding<span class="token punctuation">.</span><span class="token function">inflate</span><span class="token punctuation">(</span>layoutInflater<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="21"></td><td><pre>        <span class="token function">setContentView</span><span class="token punctuation">(</span>binding<span class="token punctuation">.</span>root<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="22"></td><td><pre></pre></td></tr><tr><td data-num="23"></td><td><pre>        sp <span class="token operator">=</span> <span class="token function">getPreferences</span><span class="token punctuation">(</span>Context<span class="token punctuation">.</span>MODE_PRIVATE<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="24"></td><td><pre></pre></td></tr><tr><td data-num="25"></td><td><pre>        <span class="token keyword">val</span> contReserved <span class="token operator">=</span> sp<span class="token punctuation">.</span><span class="token function">getInt</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"count"</span></span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="26"></td><td><pre></pre></td></tr><tr><td data-num="27"></td><td><pre>        viewModel <span class="token operator">=</span>  <span class="token function">MainViewModelFactory</span><span class="token punctuation">(</span>contReserved<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span>MainViewModel<span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">.</span>java<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="28"></td><td><pre></pre></td></tr><tr><td data-num="29"></td><td><pre>        viewModel<span class="token punctuation">.</span>counter<span class="token punctuation">.</span><span class="token function">observe</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">,</span> Observer<span class="token punctuation">&#123;</span> count <span class="token operator">-></span></pre></td></tr><tr><td data-num="30"></td><td><pre>            binding<span class="token punctuation">.</span>infoText<span class="token punctuation">.</span>text <span class="token operator">=</span> count<span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="31"></td><td><pre>        <span class="token punctuation">&#125;</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="32"></td><td><pre></pre></td></tr><tr><td data-num="33"></td><td><pre>        binding<span class="token punctuation">.</span>testViewModel<span class="token punctuation">.</span><span class="token function">setOnClickListener</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="34"></td><td><pre>            viewModel<span class="token punctuation">.</span><span class="token function">plusOne</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="35"></td><td><pre>        <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="36"></td><td><pre></pre></td></tr><tr><td data-num="37"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="38"></td><td><pre></pre></td></tr><tr><td data-num="39"></td><td><pre>    <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">onPause</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="40"></td><td><pre>        <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">onPause</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="41"></td><td><pre></pre></td></tr><tr><td data-num="42"></td><td><pre>        sp<span class="token punctuation">.</span><span class="token function">edit</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="43"></td><td><pre>            <span class="token function">putInt</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"count"</span></span><span class="token punctuation">,</span> viewModel<span class="token punctuation">.</span>counter<span class="token punctuation">.</span>value<span class="token operator">?:</span><span class="token number">0</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="44"></td><td><pre>        <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="45"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="46"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="47"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">104</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>然后就可以使用如下语法结构的 observe () 方法了：</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre>viewModel<span class="token punctuation">.</span>counter<span class="token punctuation">.</span><span class="token function">observe</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span> count <span class="token operator">-></span></pre></td></tr><tr><td data-num="2"></td><td><pre>    infoText<span class="token punctuation">.</span>text <span class="token operator">=</span> count<span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">105</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>可以看到，这里先将原来的 counter 变量改名为 <code>_counter</code>  变量，并给它加上 private 修饰符，这样 <code>_counter</code>  变量对于外部就是不可见的了。然后我们又新定义了一个 counter 变量，将它的类型声明为不可变的 LiveData，并在它的 get () 属性方法中返回 <code>_counter</code>  变量。</p>
</li>
<li>
<p>这样，当外部调用 counter 变量时，实际上获得的就是 <code>_counter</code>  的实例，但是无法给 counter 设置数据，从而保证了 ViewModel 的数据封装性</p>
</li>
</ul>
<h4 id="map和switchmap"><a class="markdownIt-Anchor" href="#map和switchmap">#</a> map 和 switchMap</h4>
<ul>
<li>
<p>LiveData 的基本用法虽说可以满足大部分的开发需求，但是当项目变得复杂之后，可能会出现一些更加特殊的需求。LiveData 为了能够应对各种不同的需求场景，提供了两种转换方法： <code>map()</code>  和 <code>switchMap()</code>  方法</p>
</li>
<li>
<p>先来看 map () 方法，这个方法的作用是将实际包含数据的 LiveData 和仅用于观察数据的 LiveData 进行转换。那么什么情况下会用到这个方法呢？下面我来举一个例子。</p>
</li>
<li>
<p>比如说有一个 User 类，User 中包含用户的姓名和年龄，定义如下：</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">data</span> <span class="token keyword">class</span> <span class="token function">User</span><span class="token punctuation">(</span><span class="token keyword">var</span> firstName<span class="token operator">:</span> String<span class="token punctuation">,</span> <span class="token keyword">var</span> lastName<span class="token operator">:</span> String<span class="token punctuation">,</span> <span class="token keyword">var</span> age<span class="token operator">:</span> Int<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">106</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>到目前为止，这和我们在上一小节中学习的内容并没有什么区别。可是如果 MainActivity 中明确只会显示用户的姓名，而完全不关心用户的年龄，那么这个时候还将整个 User 类型的 LiveData 暴露给外部，就显得不那么合适了。</p>
</li>
<li>
<p>而 map () 方法就是专门用于解决这种问题的，它可以将 User 类型的 LiveData 自由地转型成任意其他类型的 LiveData，下面我们来看一下具体的用法：</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">class</span> <span class="token function">MainViewModel</span><span class="token punctuation">(</span>countReserved<span class="token operator">:</span> Int<span class="token punctuation">)</span> <span class="token operator">:</span> <span class="token function">ViewModel</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre></pre></td></tr><tr><td data-num="3"></td><td><pre>    <span class="token keyword">private</span> <span class="token keyword">val</span> userLiveData <span class="token operator">=</span> MutableLiveData<span class="token operator">&lt;</span>User<span class="token operator">></span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="4"></td><td><pre></pre></td></tr><tr><td data-num="5"></td><td><pre>    <span class="token keyword">val</span> userName<span class="token operator">:</span> LiveData<span class="token operator">&lt;</span>String<span class="token operator">></span> <span class="token operator">=</span> Transformations<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span>userLiveData<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span> user <span class="token operator">-></span></pre></td></tr><tr><td data-num="6"></td><td><pre>        <span class="token string-literal singleline"><span class="token string">"</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">$&#123;</span><span class="token expression">user<span class="token punctuation">.</span>firstName</span><span class="token interpolation-punctuation punctuation">&#125;</span></span><span class="token string"> </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">$&#123;</span><span class="token expression">user<span class="token punctuation">.</span>lastName</span><span class="token interpolation-punctuation punctuation">&#125;</span></span><span class="token string">"</span></span></pre></td></tr><tr><td data-num="7"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="8"></td><td><pre>    <span class="token operator">..</span><span class="token punctuation">.</span></pre></td></tr><tr><td data-num="9"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="10"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">107</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>这里我们在 Repository 类中添加了一个 <code>getUser()</code>  方法，这个方法接收一个 userId 参数，按照正常的编码逻辑，我们应该传入的 userId 参数去服务器请求或者到数据库中查找相应的 User 对象，但是这里只是模拟实例，因此每次将传入的 userId 当做用户名来创建一个新的 User 对象即可</p>
</li>
<li>
<p>需要注意的是， <code>getUser()</code>  方法返回的是一个包含 User 数据的 LiveData 对象，而且每次调用 <code>getUser()</code>  方法都会返回一个新的 LiveData 实例</p>
</li>
<li>
<p>然后我们在 MainViewModel 中也定义一个 <code>getUser()</code>  方法，并且让他调用 Repository 的 <code>getUser()</code>  方法来获取 LiveData 对象</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">class</span> <span class="token function">MainViewModel</span><span class="token punctuation">(</span>countReserved <span class="token operator">:</span> Int<span class="token punctuation">)</span> <span class="token operator">:</span> <span class="token function">ViewModel</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre>    <span class="token operator">..</span><span class="token punctuation">.</span></pre></td></tr><tr><td data-num="3"></td><td><pre>    <span class="token keyword">fun</span> <span class="token function">getUser</span><span class="token punctuation">(</span>userId <span class="token operator">:</span> String<span class="token punctuation">)</span> <span class="token operator">:</span> LiveData<span class="token operator">&lt;</span>User<span class="token operator">></span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="4"></td><td><pre>        <span class="token keyword">return</span> Repositroy<span class="token punctuation">.</span><span class="token function">getUser</span><span class="token punctuation">(</span>userId<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="5"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="6"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="7"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">108</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>请注意，这么做是完全错误的，因为每次调用 <code>getUser()</code>  方法返回的都是一个新的 LiveData 实例，而上述写法会一直观察老的 LiveData 实例，从而根本无法观察到数据的变化，你会发现这种情况下的 LiveData 是不可观察的</p>
</li>
<li>
<p>这个时候 <code>switchMap()</code>  方法就可以派上用场了，正如前面所说，他的使用场景非常固定：如果 ViewModel 中的某个 LiveData 对象是调用另外的方法获取的，那么我们就可以借助 <code>switchMap</code>  方法将这个 LiveData 对象转换成另外一个可观察的 LiveData</p>
</li>
<li>
<p>修改 MainViewModel 中的代码，如下所示：</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">class</span> <span class="token function">MainViewModel</span><span class="token punctuation">(</span>countReserved <span class="token operator">:</span> Int<span class="token punctuation">)</span> <span class="token operator">:</span> <span class="token function">ViewModel</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre>    <span class="token operator">..</span><span class="token punctuation">.</span></pre></td></tr><tr><td data-num="3"></td><td><pre>    </pre></td></tr><tr><td data-num="4"></td><td><pre>    <span class="token keyword">private</span> <span class="token keyword">val</span> userIdLiveData <span class="token operator">=</span> MutableLiveData<span class="token operator">&lt;</span>String<span class="token operator">></span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="5"></td><td><pre>    </pre></td></tr><tr><td data-num="6"></td><td><pre>    <span class="token keyword">val</span> user<span class="token operator">:</span> LiveData<span class="token operator">&lt;</span>User<span class="token operator">></span> <span class="token operator">=</span> Transformations<span class="token punctuation">.</span><span class="token function">switchMap</span><span class="token punctuation">(</span>userIdLiveData<span class="token punctuation">)</span><span class="token punctuation">&#123;</span> userId <span class="token operator">-></span></pre></td></tr><tr><td data-num="7"></td><td><pre>               Repositroy<span class="token punctuation">.</span><span class="token function">getUser</span><span class="token punctuation">(</span>userId<span class="token punctuation">)</span>                   <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="8"></td><td><pre>    </pre></td></tr><tr><td data-num="9"></td><td><pre>    <span class="token keyword">fun</span> <span class="token function">getUser</span><span class="token punctuation">(</span>userId <span class="token operator">:</span> String<span class="token punctuation">)</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="10"></td><td><pre>        userIdLiveData<span class="token punctuation">.</span>value <span class="token operator">=</span> userId</pre></td></tr><tr><td data-num="11"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="12"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="13"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">109</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>具体的用法就是这样了，我们在 “Get User” 按钮的点击事件中使用随机函数生成了一个 userId，然后调用 MainViewModel 的 <code>getUser()</code>  方法来获取用户数据，但是这个方法现在不会有任何返回值了。等数据获取完成之后，可观察 LiveData 对象的 observe () 方法将会得到通知，我们在这里将获取的用户名显示到界面上。</p>
</li>
<li>
<p>最后再介绍一个我当初学习 switchMap () 方法时产生疑惑的地方。在刚才的例子当中，我们调用 MainViewModel 的 getUser () 方法时传入了一个 userId 参数，为了能够观察这个参数的数据变化，又构建了一个 userIdLiveData，然后在 switchMap () 方法中再去观察这个 LiveData 对象就可以了。但是 ViewModel 中某个获取数据的方法有可能是没有参数的，这个时候代码应该怎么写呢？</p>
</li>
<li>
<p>其实这个问题并没有想象中复杂，写法基本上和原来是相同的，只是在没有可观察数据的情况下，我们需要创建一个空的 LiveData 对象，示例写法如下：</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">class</span> MyViewModel <span class="token operator">:</span> <span class="token function">ViewModel</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre></pre></td></tr><tr><td data-num="3"></td><td><pre>    <span class="token keyword">private</span> <span class="token keyword">val</span> refreshLiveData <span class="token operator">=</span> MutableLiveData<span class="token operator">&lt;</span>Any<span class="token operator">?</span><span class="token operator">></span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="4"></td><td><pre></pre></td></tr><tr><td data-num="5"></td><td><pre>    <span class="token keyword">val</span> refreshResult <span class="token operator">=</span> Transformations<span class="token punctuation">.</span><span class="token function">switchMap</span><span class="token punctuation">(</span>refreshLiveData<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="6"></td><td><pre>        Repository<span class="token punctuation">.</span><span class="token function">refresh</span><span class="token punctuation">(</span><span class="token punctuation">)</span>  <span class="token comment">// 假设 Repository 中已经定义了 refresh () 方法</span></pre></td></tr><tr><td data-num="7"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="8"></td><td><pre></pre></td></tr><tr><td data-num="9"></td><td><pre>    <span class="token keyword">fun</span> <span class="token function">refresh</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="10"></td><td><pre>        refreshLiveData<span class="token punctuation">.</span>value <span class="token operator">=</span> refreshLiveData<span class="token punctuation">.</span>value</pre></td></tr><tr><td data-num="11"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="12"></td><td><pre></pre></td></tr><tr><td data-num="13"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="14"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">110</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>这里新增了一个 <code>kotlin-kapt</code>  插件，同时在 <code>dependencies</code>  闭包中添加了两个 Room 的依赖库，由于 Room 会根据我们在项目中声明的注解来动态生成代码，因此这里一定要使用 kapt 引入 Romm 的编译时注解库，而用编译时注解功能则一定一定要先添加 kotlin-kapt 插件，注意，kapt 只能在 Kotlin 项目中使用，如果是 Java 项目的话，使用 <code>anitationProcessor</code>  即可</p>
</li>
<li>
<p>下面我们就按照刚擦介绍的 Room 的三个组成部分一一来进行实现，首先是定义 Entity，也就是实体类</p>
</li>
<li>
<p>一个良好的 数据库编程建议是，给每个实体类都添加一个 id 字段，并将这个字段设置为主键，于是我们对 User 类进行如下改造，并完成实体类的声明</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token annotation builtin">@Entity</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">data</span> <span class="token keyword">class</span> <span class="token function">User</span><span class="token punctuation">(</span><span class="token keyword">val</span> firstName <span class="token operator">:</span> String<span class="token punctuation">,</span> <span class="token keyword">val</span> lastName <span class="token operator">:</span> String<span class="token punctuation">,</span> <span class="token keyword">var</span> age <span class="token operator">:</span> Int<span class="token punctuation">)</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="3"></td><td><pre>    <span class="token annotation builtin">@PrimaryKey</span><span class="token punctuation">(</span>autoGenerate <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="4"></td><td><pre>    <span class="token keyword">var</span> id <span class="token operator">:</span> Long <span class="token operator">=</span> <span class="token number">0</span></pre></td></tr><tr><td data-num="5"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="6"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">111</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>UserDao 接口的上面上用了一个 <code>@Dao</code>  注解，这样 Room 才能将他识别成一个 Dao，UserDao 的内部就是根据业务需求对各种数据库操作进行的封装，数据库操作通常有增删改查这四种，因此 Room 提供了 <code>@Insert</code> 、 <code>@Delete</code> 、 <code>@update</code> 、和 <code>@Query</code>  这四种相应的注解</p>
</li>
<li>
<p>可以看到， <code>InsertUser()</code>  方法上面石宏了 <code>@Insert</code>  注解，表示会将参数中传入 User 对象插入数据库中，插入完成后还会将自动生成的主键 id 值返回， <code>updateUser()</code>  方法上面使用了 <code>@Update</code>  注解，表示会将参数中传入的 USer 对象更新到数据库当中， <code>delteUser()</code>  方法上面使用了 <code>@Delete</code>  注解，表示会将参数传入的 User 独享从数据库中删除</p>
</li>
<li>
<p>但是如果想要从数据库中查询数据，或者使用非实体类参数来增删改查，那么就必须编写 SQL 语句了，比如说我们在 UserDao 接口重定义了一个 <code>loadAllUsers()</code>  方法，用于从数据库中查询所有的用户，如果只是用一个 <code>@Query</code>  注解，Room 将无法知道我们想要查询那些数据，因为此必须在 <code>@Query</code>  注解中编写出具体的 SQL 语句才行，我们还可以将方法中传入的参数指定到 SQL 语句道中，比如 <code>laodUsersOlderThan()</code>  方法就可以查询所有年龄大于指定参数的用户，另外，如果是使用非实体类的参数来增删改数据，那么也要编写 SQL 语句才行，而且这个时候不能使用 <code>@Insert</code> 、 <code>@Delete</code>  或 <code>@Update</code>  注解，而是都要使用 <code>@Query</code>  注解才行，参考 <code>deleteUserByLastName()</code>  方法的写法</p>
</li>
<li>
<p>接下来我们进入最后一个环节，定义 Database，这部分内容 的写法是非常固定的，只需要定义好三个部分的内容，数据库的版本号，包含哪些实体类，以及提供 Dao 层的访问实例，新建一个 <code>AppDatabase.kt</code>  文件，代码如下所示:</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">package</span> cn<span class="token punctuation">.</span>wenhe9<span class="token punctuation">.</span>testmenu</pre></td></tr><tr><td data-num="2"></td><td><pre></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token keyword">import</span> android<span class="token punctuation">.</span>content<span class="token punctuation">.</span>Context</pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token keyword">import</span> androidx<span class="token punctuation">.</span>room<span class="token punctuation">.</span>Database</pre></td></tr><tr><td data-num="5"></td><td><pre><span class="token keyword">import</span> androidx<span class="token punctuation">.</span>room<span class="token punctuation">.</span>Room</pre></td></tr><tr><td data-num="6"></td><td><pre><span class="token keyword">import</span> androidx<span class="token punctuation">.</span>room<span class="token punctuation">.</span>RoomDatabase</pre></td></tr><tr><td data-num="7"></td><td><pre></pre></td></tr><tr><td data-num="8"></td><td><pre><span class="token comment">/**</pre></td></tr><tr><td data-num="9"></td><td><pre> *@author DuJinliang</pre></td></tr><tr><td data-num="10"></td><td><pre> *2021/9/30</pre></td></tr><tr><td data-num="11"></td><td><pre> */</span></pre></td></tr><tr><td data-num="12"></td><td><pre></pre></td></tr><tr><td data-num="13"></td><td><pre><span class="token annotation builtin">@Database</span><span class="token punctuation">(</span>version <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">,</span> entities <span class="token operator">=</span> <span class="token punctuation">[</span>User<span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">]</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="14"></td><td><pre><span class="token keyword">abstract</span> <span class="token keyword">class</span> AppDatabase <span class="token operator">:</span> <span class="token function">RoomDatabase</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="15"></td><td><pre></pre></td></tr><tr><td data-num="16"></td><td><pre>    <span class="token keyword">abstract</span> <span class="token keyword">fun</span> <span class="token function">userDao</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">:</span> UserDao</pre></td></tr><tr><td data-num="17"></td><td><pre></pre></td></tr><tr><td data-num="18"></td><td><pre>    <span class="token keyword">companion</span> <span class="token keyword">object</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="19"></td><td><pre>        <span class="token keyword">private</span> <span class="token keyword">var</span> instance <span class="token operator">:</span> AppDatabase<span class="token operator">?</span> <span class="token operator">=</span> <span class="token keyword">null</span></pre></td></tr><tr><td data-num="20"></td><td><pre></pre></td></tr><tr><td data-num="21"></td><td><pre>        <span class="token annotation builtin">@Synchronized</span></pre></td></tr><tr><td data-num="22"></td><td><pre>        <span class="token keyword">fun</span> <span class="token function">getDatabase</span><span class="token punctuation">(</span>context <span class="token operator">:</span> Context<span class="token punctuation">)</span> <span class="token operator">:</span> AppDatabase<span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="23"></td><td><pre>            instance<span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">let</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="24"></td><td><pre>                <span class="token keyword">return</span> it</pre></td></tr><tr><td data-num="25"></td><td><pre>            <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="26"></td><td><pre></pre></td></tr><tr><td data-num="27"></td><td><pre>            <span class="token keyword">return</span> Room<span class="token punctuation">.</span><span class="token function">databaseBuilder</span><span class="token punctuation">(</span>context<span class="token punctuation">.</span>applicationContext<span class="token punctuation">,</span> AppDatabase<span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">.</span>java<span class="token punctuation">,</span> <span class="token string-literal singleline"><span class="token string">"app_database"</span></span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="28"></td><td><pre>                <span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">apply</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="29"></td><td><pre>                    instance <span class="token operator">=</span> <span class="token keyword">this</span></pre></td></tr><tr><td data-num="30"></td><td><pre>                <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="31"></td><td><pre>        <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="32"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="33"></td><td><pre></pre></td></tr><tr><td data-num="34"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="35"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">112</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>另外，由于数据库操作属于耗时操作，Room 默认是不允许在主线程中进行数据库操作的，因此上述代码中我们将增删改查的功能都放到了子线程中。不过为了方便测试，Room 还提供了一个更加简单的方法，如下所示：</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre>Room<span class="token punctuation">.</span><span class="token function">databaseBuilder</span><span class="token punctuation">(</span>context<span class="token punctuation">.</span>applicationContext<span class="token punctuation">,</span> AppDatabase<span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">.</span>java<span class="token punctuation">,</span><span class="token string-literal singleline"><span class="token string">"app_database"</span></span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="2"></td><td><pre>    <span class="token punctuation">.</span><span class="token function">allowMainThreadQueries</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="3"></td><td><pre>    <span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">113</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>在构建 AppDatabase 实例的时候，加入一个 fallbackToDestructiveMigration () 方法。这样只要数据库进行了升级，Room 就会将当前的数据库销毁，然后再重新创建，随之而来的副作用就是之前数据库中的所有数据就全部丢失了。</p>
</li>
<li>
<p>假如产品还在开发和测试阶段，这个方法是可以使用的，但是一旦产品对外发布之后，如果造成了用户数据丢失，那可是严重的事故。因此接下来我们还是老老实实学习一下在 Room 中升级数据库的正规写法。</p>
</li>
<li>
<p>随着业务逻辑的升级，现在我们打算在数据库中添加一张 Book 表，那么首先要做的就是创建一个 Book 的实体类，如下所示：</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token annotation builtin">@Entity</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">data</span> <span class="token keyword">class</span> <span class="token function">Book</span><span class="token punctuation">(</span><span class="token keyword">var</span> name<span class="token operator">:</span> String<span class="token punctuation">,</span> <span class="token keyword">var</span> pages<span class="token operator">:</span> Int<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="3"></td><td><pre></pre></td></tr><tr><td data-num="4"></td><td><pre>    <span class="token annotation builtin">@PrimaryKey</span><span class="token punctuation">(</span>autoGenerate <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="5"></td><td><pre>    <span class="token keyword">var</span> id<span class="token operator">:</span> Long <span class="token operator">=</span> <span class="token number">0</span></pre></td></tr><tr><td data-num="6"></td><td><pre></pre></td></tr><tr><td data-num="7"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="8"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">114</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>接下来修改 AppDatabase 中的代码，在里面编写数据库升级的逻辑，如下所示：</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token annotation builtin">@Database</span><span class="token punctuation">(</span>version <span class="token operator">=</span> <span class="token number">2</span><span class="token punctuation">,</span> entities <span class="token operator">=</span> <span class="token punctuation">[</span>User<span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">,</span> Book<span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">]</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">abstract</span> <span class="token keyword">class</span> AppDatabase <span class="token operator">:</span> <span class="token function">RoomDatabase</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="3"></td><td><pre></pre></td></tr><tr><td data-num="4"></td><td><pre>    <span class="token keyword">abstract</span> <span class="token keyword">fun</span> <span class="token function">userDao</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">:</span> UserDao</pre></td></tr><tr><td data-num="5"></td><td><pre></pre></td></tr><tr><td data-num="6"></td><td><pre>    <span class="token keyword">abstract</span> <span class="token keyword">fun</span> <span class="token function">bookDao</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">:</span> BookDao</pre></td></tr><tr><td data-num="7"></td><td><pre></pre></td></tr><tr><td data-num="8"></td><td><pre>    <span class="token keyword">companion</span> <span class="token keyword">object</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="9"></td><td><pre></pre></td></tr><tr><td data-num="10"></td><td><pre>        <span class="token keyword">val</span> MIGRATION_1_2 <span class="token operator">=</span> <span class="token keyword">object</span> <span class="token operator">:</span> <span class="token function">Migration</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="11"></td><td><pre>            <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">migrate</span><span class="token punctuation">(</span>database<span class="token operator">:</span> SupportSQLiteDatabase<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="12"></td><td><pre>                     database<span class="token punctuation">.</span><span class="token function">execSQL</span><span class="token punctuation">(</span>"create table <span class="token function">Book</span> <span class="token punctuation">(</span>id integer primary</pre></td></tr><tr><td data-num="13"></td><td><pre>                         key autoincrement not <span class="token keyword">null</span><span class="token punctuation">,</span> name text not <span class="token keyword">null</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="14"></td><td><pre>                         pages integer not <span class="token keyword">null</span><span class="token punctuation">)</span>"<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="15"></td><td><pre>            <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="16"></td><td><pre>        <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="17"></td><td><pre></pre></td></tr><tr><td data-num="18"></td><td><pre>        <span class="token keyword">private</span> <span class="token keyword">var</span> instance<span class="token operator">:</span> AppDatabase<span class="token operator">?</span> <span class="token operator">=</span> <span class="token keyword">null</span></pre></td></tr><tr><td data-num="19"></td><td><pre></pre></td></tr><tr><td data-num="20"></td><td><pre>        <span class="token keyword">fun</span> <span class="token function">getDatabase</span><span class="token punctuation">(</span>context<span class="token operator">:</span> Context<span class="token punctuation">)</span><span class="token operator">:</span> AppDatabase <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="21"></td><td><pre>            instance<span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">let</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="22"></td><td><pre>                <span class="token keyword">return</span> it</pre></td></tr><tr><td data-num="23"></td><td><pre>            <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="24"></td><td><pre>            <span class="token keyword">return</span> Room<span class="token punctuation">.</span><span class="token function">databaseBuilder</span><span class="token punctuation">(</span>context<span class="token punctuation">.</span>applicationContext<span class="token punctuation">,</span></pre></td></tr><tr><td data-num="25"></td><td><pre>                    AppDatabase<span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">.</span>java<span class="token punctuation">,</span> <span class="token string-literal singleline"><span class="token string">"app_database"</span></span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="26"></td><td><pre>                <span class="token punctuation">.</span><span class="token function">addMigrations</span><span class="token punctuation">(</span>MIGRATION_1_2<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="27"></td><td><pre>                <span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">apply</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="28"></td><td><pre>                instance <span class="token operator">=</span> <span class="token keyword">this</span></pre></td></tr><tr><td data-num="29"></td><td><pre>            <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="30"></td><td><pre>        <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="31"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="32"></td><td><pre></pre></td></tr><tr><td data-num="33"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="34"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">115</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>既然实体类的字段发生了变动，那么对应的数据库表也必须升级了，所以这里修改 AppDatabase 中的代码，如下所示：</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token annotation builtin">@Database</span><span class="token punctuation">(</span>version <span class="token operator">=</span> <span class="token number">3</span><span class="token punctuation">,</span> entities <span class="token operator">=</span> <span class="token punctuation">[</span>User<span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">,</span> Book<span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">]</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">abstract</span> <span class="token keyword">class</span> AppDatabase <span class="token operator">:</span> <span class="token function">RoomDatabase</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="3"></td><td><pre>   <span class="token operator">..</span><span class="token punctuation">.</span></pre></td></tr><tr><td data-num="4"></td><td><pre>    <span class="token keyword">companion</span> <span class="token keyword">object</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="5"></td><td><pre>        <span class="token operator">..</span><span class="token punctuation">.</span></pre></td></tr><tr><td data-num="6"></td><td><pre>        <span class="token keyword">val</span> MIGRATION_2_3 <span class="token operator">=</span> <span class="token keyword">object</span> <span class="token operator">:</span> <span class="token function">Migration</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">3</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="7"></td><td><pre>            <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">migrate</span><span class="token punctuation">(</span>database<span class="token operator">:</span> SupportSQLiteDatabase<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="8"></td><td><pre>                database<span class="token punctuation">.</span><span class="token function">execSQL</span><span class="token punctuation">(</span>"alter table Book add column author text not <span class="token keyword">null</span></pre></td></tr><tr><td data-num="9"></td><td><pre>                    default 'unknown'"<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="10"></td><td><pre>            <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="11"></td><td><pre>        <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="12"></td><td><pre></pre></td></tr><tr><td data-num="13"></td><td><pre>        <span class="token keyword">private</span> <span class="token keyword">var</span> instance<span class="token operator">:</span> AppDatabase<span class="token operator">?</span> <span class="token operator">=</span> <span class="token keyword">null</span></pre></td></tr><tr><td data-num="14"></td><td><pre></pre></td></tr><tr><td data-num="15"></td><td><pre>        <span class="token keyword">fun</span> <span class="token function">getDatabase</span><span class="token punctuation">(</span>context<span class="token operator">:</span> Context<span class="token punctuation">)</span><span class="token operator">:</span> AppDatabase <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="16"></td><td><pre>            <span class="token operator">..</span><span class="token punctuation">.</span></pre></td></tr><tr><td data-num="17"></td><td><pre>            <span class="token keyword">return</span> Room<span class="token punctuation">.</span><span class="token function">databaseBuilder</span><span class="token punctuation">(</span>context<span class="token punctuation">.</span>applicationContext<span class="token punctuation">,</span></pre></td></tr><tr><td data-num="18"></td><td><pre>                AppDatabase<span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">.</span>java<span class="token punctuation">,</span> <span class="token string-literal singleline"><span class="token string">"app_database"</span></span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="19"></td><td><pre>                <span class="token punctuation">.</span><span class="token function">addMigrations</span><span class="token punctuation">(</span>MIGRATION_1_2<span class="token punctuation">,</span> MIGRATION_2_3<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="20"></td><td><pre>                <span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">apply</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="21"></td><td><pre>                instance <span class="token operator">=</span> <span class="token keyword">this</span></pre></td></tr><tr><td data-num="22"></td><td><pre>            <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="23"></td><td><pre>        <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="24"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="25"></td><td><pre></pre></td></tr><tr><td data-num="26"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="27"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">116</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>WorkManager 的基本用法其实非常简单，主要分为以下三步</p>
<ol>
<li>定义一个后台任务，并实现具体的任务逻辑</li>
<li>配置该后台任务的运行条件和约束信息，并构建后任务请求</li>
<li>将该后台任务请求传入 WorkManager 的 <code>enqueue()</code>  方法，系统会在合适的时间运行</li>
</ol>
</li>
<li>
<p>第一步要定义一个后台任务，这里创建一个 SimpleWorker 类，代码如下所示：</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">package</span> cn<span class="token punctuation">.</span>wenhe9<span class="token punctuation">.</span>testmenu</pre></td></tr><tr><td data-num="2"></td><td><pre></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token keyword">import</span> android<span class="token punctuation">.</span>content<span class="token punctuation">.</span>Context</pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token keyword">import</span> android<span class="token punctuation">.</span>util<span class="token punctuation">.</span>Log</pre></td></tr><tr><td data-num="5"></td><td><pre><span class="token keyword">import</span> androidx<span class="token punctuation">.</span>work<span class="token punctuation">.</span>Worker</pre></td></tr><tr><td data-num="6"></td><td><pre><span class="token keyword">import</span> androidx<span class="token punctuation">.</span>work<span class="token punctuation">.</span>WorkerParameters</pre></td></tr><tr><td data-num="7"></td><td><pre></pre></td></tr><tr><td data-num="8"></td><td><pre><span class="token comment">/**</pre></td></tr><tr><td data-num="9"></td><td><pre> *@author DuJinliang</pre></td></tr><tr><td data-num="10"></td><td><pre> *2021/9/30</pre></td></tr><tr><td data-num="11"></td><td><pre> */</span></pre></td></tr><tr><td data-num="12"></td><td><pre><span class="token keyword">class</span> <span class="token function">SimpleWorker</span><span class="token punctuation">(</span>context <span class="token operator">:</span> Context<span class="token punctuation">,</span> params <span class="token operator">:</span> WorkerParameters<span class="token punctuation">)</span> <span class="token operator">:</span> <span class="token function">Worker</span><span class="token punctuation">(</span>context<span class="token punctuation">,</span> params<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="13"></td><td><pre>    <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">doWork</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">:</span> Result <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="14"></td><td><pre>        Log<span class="token punctuation">.</span><span class="token function">d</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"SimpleWorker"</span></span><span class="token punctuation">,</span> <span class="token string-literal singleline"><span class="token string">"do work in SimpleWorker"</span></span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="15"></td><td><pre>        <span class="token keyword">return</span> Result<span class="token punctuation">.</span><span class="token function">success</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="16"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="17"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="18"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">117</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>可以看到，只需要把刚才创建的后台任务所对应的的 Class 对象传入 <code>OneTimeWorkRequest.Builder</code>  的构造函数中，然后调用 <code>build()</code>  方法即可完成构建</p>
</li>
<li>
<p><code>OneTimeWorkRequest.Builder</code>  是 <code>WorkRequest.Builder</code>  的子类，用于构建单次运行的后台请求， <code>WorkRequest.Builder</code>  还有另外一个子类 <code>PeriodicWorkRequest.Builder</code> ，可用于构建周期性运行的后台任务请求，但是为了降低设备的性能消耗， <code>PeriodicWorkRequest.Builder</code>  构造函数中传入的运行周期补鞥呢短于 15 分钟，示例代码如下：</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">val</span> request <span class="token operator">=</span> PeriodicWorkRequest<span class="token punctuation">.</span><span class="token function">Builder</span><span class="token punctuation">(</span>SimpleWorker<span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">.</span>java<span class="token punctuation">,</span> <span class="token number">15</span><span class="token punctuation">,</span> TimeUnit<span class="token punctuation">.</span>MINUTES<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">118</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">class</span> MainActivity <span class="token operator">:</span> <span class="token function">AppCompatActivity</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre>    <span class="token operator">..</span><span class="token punctuation">.</span></pre></td></tr><tr><td data-num="3"></td><td><pre>    <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">onCreate</span><span class="token punctuation">(</span>savedInstanceState<span class="token operator">:</span> Bundle<span class="token operator">?</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="4"></td><td><pre>        <span class="token operator">..</span><span class="token punctuation">.</span></pre></td></tr><tr><td data-num="5"></td><td><pre>        doWorkBtn<span class="token punctuation">.</span><span class="token function">setOnClickListener</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="6"></td><td><pre>            <span class="token keyword">val</span> request <span class="token operator">=</span> OneTimeWorkRequest<span class="token punctuation">.</span><span class="token function">Builder</span><span class="token punctuation">(</span>SimpleWorker<span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">.</span>java<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="7"></td><td><pre>            WorkManager<span class="token punctuation">.</span><span class="token function">getInstance</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">enqueue</span><span class="token punctuation">(</span>request<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="8"></td><td><pre>        <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="9"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="10"></td><td><pre>    <span class="token operator">..</span><span class="token punctuation">.</span></pre></td></tr><tr><td data-num="11"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="12"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">119</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
<li>
<p>这就表示我们希望让 SimpleWorker 这个后台任务在 5 分钟后运行。你可以自由选择时间的单位，毫秒、秒、分钟、小时、天都可以。</p>
</li>
<li>
<p>可以控制运行时间之后，我们再增加一些别的功能，比如说给后台任务请求添加标签：</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">val</span> request <span class="token operator">=</span> OneTimeWorkRequest<span class="token punctuation">.</span><span class="token function">Builder</span><span class="token punctuation">(</span>SimpleWorker<span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">.</span>java<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="2"></td><td><pre>    <span class="token operator">..</span><span class="token punctuation">.</span></pre></td></tr><tr><td data-num="3"></td><td><pre>    <span class="token punctuation">.</span><span class="token function">addTag</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"simple"</span></span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="4"></td><td><pre>    <span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="5"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">120</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>当然，即使没有标签，也可以通过 id 来取消后台任务请求：</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre>WorkManager<span class="token punctuation">.</span><span class="token function">getInstance</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">cancelWorkById</span><span class="token punctuation">(</span>request<span class="token punctuation">.</span>id<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">121</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>另外，我们在上一小节中讲到，如果后台任务的 <code>doWork()</code>  方法中返回了 <code>Result.retry()</code> ，那么是可以结合 <code>setBackoffCriteria()</code>  方法来重新执行任务的，具体代码如下所示：</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">val</span> request <span class="token operator">=</span> OneTimeWorkRequest<span class="token punctuation">.</span><span class="token function">Builder</span><span class="token punctuation">(</span>SimpleWorker<span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">.</span>java<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="2"></td><td><pre>    <span class="token operator">..</span><span class="token punctuation">.</span></pre></td></tr><tr><td data-num="3"></td><td><pre>    <span class="token punctuation">.</span><span class="token function">setBackoffCriteria</span><span class="token punctuation">(</span>BackoffPolicy<span class="token punctuation">.</span>LINEAR<span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">,</span> TimeUnit<span class="token punctuation">.</span>SECONDS<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="4"></td><td><pre>    <span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="5"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">122</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>这里调用了 getWorkInfoByIdLiveData () 方法，并传入后台任务请求的 id，会返回一个 LiveData 对象。然后我们就可以调用 LiveData 对象的 observe () 方法来观察数据变化了，以此监听后台任务的运行结果。另外，你也可以调用 <code>getWorkInfosByTagLiveData()</code>  方法，监听同一标签名下所有后台任务请求的运行结果，用法是差不多的，这里就不再进行解释了。</p>
</li>
<li>
<p>接下来，我们再来看一下 WorkManager 中比较有特色的一个功能 —— 链式任务。</p>
</li>
<li>
<p>假设这里定义了 3 个独立的后台任务：同步数据、压缩数据和上传数据。现在我们想要实现先同步、再压缩、最后上传的功能，就可以借助链式任务来实现，代码示例如下：</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">val</span> sync <span class="token operator">=</span> <span class="token operator">..</span><span class="token punctuation">.</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">val</span> compress <span class="token operator">=</span> <span class="token operator">..</span><span class="token punctuation">.</span></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token keyword">val</span> upload <span class="token operator">=</span> <span class="token operator">..</span><span class="token punctuation">.</span></pre></td></tr><tr><td data-num="4"></td><td><pre>WorkManager<span class="token punctuation">.</span><span class="token function">getInstance</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="5"></td><td><pre>    <span class="token punctuation">.</span><span class="token function">beginWith</span><span class="token punctuation">(</span>sync<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="6"></td><td><pre>    <span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span>compress<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="7"></td><td><pre>    <span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span>upload<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="8"></td><td><pre>    <span class="token punctuation">.</span><span class="token function">enqueue</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="9"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">123</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
</ul>
<h3 id="button"><a class="markdownIt-Anchor" href="#button">#</a> Button</h3>
<ul>
<li>
<p>按钮</p>
</li>
<li>
<p>如果按钮的内容使用的是英文，那么运行后的显示会全部是大写字母，如果需要原样使用的话，则需要指定一个属性</p>
<ul>
<li>
<div class="highlight-container" data-rel="Xml"><figure class="iseeu highlight xml"><figcaption data-lang="XML"></figcaption><table><tr><td data-num="1"></td><td><pre>android:textAllCaps="false"</pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token comment"><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">    </span><br><span class="line"></span><br><span class="line">### EditText</span><br><span class="line"></span><br><span class="line">- 允许用户在控件里输入和编辑内容</span><br><span class="line">- 可以使用``android:hint``来指定输入框默认显示的内容，类似``placeHolder``</span><br><span class="line">- 可以使用``android:maxLines``来指定输入框的最大行数，当输入的内容超过两行时，文本就会像上滚动，EditText则不会继续拉伸</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">### ImageView</span><br><span class="line"></span><br><span class="line">- 用于在界面上展示图片</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">### ProgressBar</span><br><span class="line"></span><br><span class="line">- 用于在界面显示一个进度条</span><br><span class="line"></span><br><span class="line">- 可以通过``style``属性指定成水平进度条，在指定成水平进度条后，还可以使用``max``属性个进度条设置一个最大值，然后在代码中动态的更改进度条的进度</span><br><span class="line"></span><br><span class="line">  - ```xml</span><br><span class="line">    &lt;Button</span><br><span class="line">            android:id=&quot;@+id/addNum&quot;</span><br><span class="line">            android:text=&quot;add Num&quot;</span><br><span class="line">            android:layout_width=&quot;match_parent&quot;</span><br><span class="line">            android:layout_height=&quot;wrap_content&quot;/&gt;</span><br><span class="line">    </span><br><span class="line">    binding.addNum.setOnClickListener &#123;</span><br><span class="line">    	binding.progressBar.progress = binding.progressBar.progress + 10</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></div></div></span></pre></td></tr></table></figure></li>
</ul>
</li>
</ul>
<h3 id="alertdialog"><a class="markdownIt-Anchor" href="#alertdialog">#</a> AlertDialog</h3>
<ul>
<li>
<p>在当前界面弹出一个对话框，这个对话框是置顶于所有界面元素之上的，能够评比其他控件的交互能力，因此 AlertDialog 一般用于提示一些非常重要的内容或者警告信息</p>
</li>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre>binding<span class="token punctuation">.</span>addNum<span class="token punctuation">.</span><span class="token function">setOnClickListener</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre>    <span class="token keyword">val</span> that <span class="token operator">=</span> <span class="token keyword">this</span></pre></td></tr><tr><td data-num="3"></td><td><pre>    AlertDialog<span class="token punctuation">.</span><span class="token function">Builder</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">apply</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="4"></td><td><pre>        <span class="token function">setTitle</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"警告"</span></span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="5"></td><td><pre>        <span class="token function">setMessage</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"你确定要这么做吗"</span></span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="6"></td><td><pre>        <span class="token function">setCancelable</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="7"></td><td><pre>        <span class="token function">setPositiveButton</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"是的"</span></span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span> dialog<span class="token punctuation">,</span> which <span class="token operator">-></span></pre></td></tr><tr><td data-num="8"></td><td><pre>                                Toast<span class="token punctuation">.</span><span class="token function">makeText</span><span class="token punctuation">(</span>that<span class="token punctuation">,</span> <span class="token string-literal singleline"><span class="token string">"我不能原谅你"</span></span><span class="token punctuation">,</span> Toast<span class="token punctuation">.</span>LENGTH_SHORT<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">show</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="9"></td><td><pre>                               <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="10"></td><td><pre></pre></td></tr><tr><td data-num="11"></td><td><pre>        <span class="token function">setNegativeButton</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"不了"</span></span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>dialog<span class="token punctuation">,</span> which <span class="token operator">-></span></pre></td></tr><tr><td data-num="12"></td><td><pre>                                Toast<span class="token punctuation">.</span><span class="token function">makeText</span><span class="token punctuation">(</span>that<span class="token punctuation">,</span> <span class="token string-literal singleline"><span class="token string">"这不是真的"</span></span><span class="token punctuation">,</span> Toast<span class="token punctuation">.</span>LENGTH_SHORT<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">show</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="13"></td><td><pre>                               <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="14"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="15"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="16"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">125</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
<h3 id="recyclerview"><a class="markdownIt-Anchor" href="#recyclerview">#</a> RecyclerView</h3>
<ul>
<li>
<p>引入 recyclerView 库</p>
<ul>
<li>
<div class="highlight-container" data-rel="Gradle"><figure class="iseeu highlight gradle"><figcaption data-lang="gradle"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">implementation</span> <span class="token string">'androidx.recyclerview:recyclerview:1.2.0'</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">126</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>准备适配器，继承自 <code>RecyclerView.Adapter</code> ，并将泛型指定为 <code>FruitAdapter.ViewHolder</code> ，其中 <code>ViewHolder</code>  是我们在 <code>FruitAdapter</code>  中定义的一个内部类</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">class</span> <span class="token function">FruitAdapter</span><span class="token punctuation">(</span><span class="token keyword">val</span> fruitList<span class="token operator">:</span> List<span class="token operator">&lt;</span>Fruit<span class="token operator">></span><span class="token punctuation">)</span> <span class="token operator">:</span></pre></td></tr><tr><td data-num="2"></td><td><pre>        RecyclerView<span class="token punctuation">.</span>Adapter<span class="token operator">&lt;</span>FruitAdapter<span class="token punctuation">.</span>ViewHolder<span class="token operator">></span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="3"></td><td><pre></pre></td></tr><tr><td data-num="4"></td><td><pre>    <span class="token keyword">inner</span> <span class="token keyword">class</span> <span class="token function">ViewHolder</span><span class="token punctuation">(</span>view<span class="token operator">:</span> View<span class="token punctuation">)</span> <span class="token operator">:</span> RecyclerView<span class="token punctuation">.</span><span class="token function">ViewHolder</span><span class="token punctuation">(</span>view<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="5"></td><td><pre>        <span class="token keyword">val</span> fruitImage<span class="token operator">:</span> ImageView <span class="token operator">=</span> view<span class="token punctuation">.</span><span class="token function">findViewById</span><span class="token punctuation">(</span>R<span class="token punctuation">.</span>id<span class="token punctuation">.</span>fruitImage<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="6"></td><td><pre>        <span class="token keyword">val</span> fruitName<span class="token operator">:</span> TextView <span class="token operator">=</span> view<span class="token punctuation">.</span><span class="token function">findViewById</span><span class="token punctuation">(</span>R<span class="token punctuation">.</span>id<span class="token punctuation">.</span>fruitName<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="7"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="8"></td><td><pre></pre></td></tr><tr><td data-num="9"></td><td><pre>    <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">onCreateViewHolder</span><span class="token punctuation">(</span>parent<span class="token operator">:</span> ViewGroup<span class="token punctuation">,</span> viewType<span class="token operator">:</span> Int<span class="token punctuation">)</span><span class="token operator">:</span> ViewHolder <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="10"></td><td><pre>        <span class="token keyword">val</span> view <span class="token operator">=</span> LayoutInflater<span class="token punctuation">.</span><span class="token function">from</span><span class="token punctuation">(</span>parent<span class="token punctuation">.</span>context<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="11"></td><td><pre>        <span class="token punctuation">.</span><span class="token function">inflate</span><span class="token punctuation">(</span>R<span class="token punctuation">.</span>layout<span class="token punctuation">.</span>fruit_item<span class="token punctuation">,</span> parent<span class="token punctuation">,</span> <span class="token boolean">false</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="12"></td><td><pre>        <span class="token keyword">return</span> <span class="token function">ViewHolder</span><span class="token punctuation">(</span>view<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="13"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="14"></td><td><pre></pre></td></tr><tr><td data-num="15"></td><td><pre>    <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">onBindViewHolder</span><span class="token punctuation">(</span>holder<span class="token operator">:</span> ViewHolder<span class="token punctuation">,</span> position<span class="token operator">:</span> Int<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="16"></td><td><pre>        <span class="token keyword">val</span> fruit <span class="token operator">=</span> fruitList<span class="token punctuation">[</span>position<span class="token punctuation">]</span></pre></td></tr><tr><td data-num="17"></td><td><pre>        holder<span class="token punctuation">.</span>fruitImage<span class="token punctuation">.</span><span class="token function">setImageResource</span><span class="token punctuation">(</span>fruit<span class="token punctuation">.</span>imageId<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="18"></td><td><pre>        holder<span class="token punctuation">.</span>fruitName<span class="token punctuation">.</span>text <span class="token operator">=</span> fruit<span class="token punctuation">.</span>name</pre></td></tr><tr><td data-num="19"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="20"></td><td><pre></pre></td></tr><tr><td data-num="21"></td><td><pre>    <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">getItemCount</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span> fruitList<span class="token punctuation">.</span>size</pre></td></tr><tr><td data-num="22"></td><td><pre></pre></td></tr><tr><td data-num="23"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="24"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">127</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>如果需要制度能够为横向滚动的话，需要给 layoutManager 指定方向</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre>layoutMangaer<span class="token punctuation">.</span>orientation <span class="token operator">=</span> LinearLayoutManager<span class="token punctuation">.</span>HORIZONTAL</pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">128</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr><tr><td data-num="3"></td><td><pre></pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token operator">-</span> 第一个参数指定列数，第二个指定方向</pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>点击事件</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">class</span> <span class="token function">FruitAdapter</span><span class="token punctuation">(</span><span class="token keyword">val</span> fruitList<span class="token operator">:</span> List<span class="token operator">&lt;</span>Fruit<span class="token operator">></span><span class="token punctuation">)</span> <span class="token operator">:</span></pre></td></tr><tr><td data-num="2"></td><td><pre>        RecyclerView<span class="token punctuation">.</span>Adapter<span class="token operator">&lt;</span>FruitAdapter<span class="token punctuation">.</span>ViewHolder<span class="token operator">></span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="3"></td><td><pre>    <span class="token operator">..</span><span class="token punctuation">.</span></pre></td></tr><tr><td data-num="4"></td><td><pre>    <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">onCreateViewHolder</span><span class="token punctuation">(</span>parent<span class="token operator">:</span> ViewGroup<span class="token punctuation">,</span> viewType<span class="token operator">:</span> Int<span class="token punctuation">)</span><span class="token operator">:</span> ViewHolder <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="5"></td><td><pre>        <span class="token keyword">val</span> view <span class="token operator">=</span> LayoutInflater<span class="token punctuation">.</span><span class="token function">from</span><span class="token punctuation">(</span>parent<span class="token punctuation">.</span>context<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="6"></td><td><pre>        <span class="token punctuation">.</span><span class="token function">inflate</span><span class="token punctuation">(</span>R<span class="token punctuation">.</span>layout<span class="token punctuation">.</span>fruit_item<span class="token punctuation">,</span> parent<span class="token punctuation">,</span> <span class="token boolean">false</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="7"></td><td><pre>        <span class="token keyword">val</span> viewHolder <span class="token operator">=</span> <span class="token function">ViewHolder</span><span class="token punctuation">(</span>view<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="8"></td><td><pre>        viewHolder<span class="token punctuation">.</span>itemView<span class="token punctuation">.</span><span class="token function">setOnClickListener</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="9"></td><td><pre>            <span class="token keyword">val</span> position <span class="token operator">=</span> viewHolder<span class="token punctuation">.</span>adapterPosition</pre></td></tr><tr><td data-num="10"></td><td><pre>            <span class="token keyword">val</span> fruit <span class="token operator">=</span> fruitList<span class="token punctuation">[</span>position<span class="token punctuation">]</span></pre></td></tr><tr><td data-num="11"></td><td><pre>            Toast<span class="token punctuation">.</span><span class="token function">makeText</span><span class="token punctuation">(</span>parent<span class="token punctuation">.</span>context<span class="token punctuation">,</span> <span class="token string-literal singleline"><span class="token string">"you clicked view </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">$&#123;</span><span class="token expression">fruit<span class="token punctuation">.</span>name</span><span class="token interpolation-punctuation punctuation">&#125;</span></span><span class="token string">"</span></span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="12"></td><td><pre>                Toast<span class="token punctuation">.</span>LENGTH_SHORT<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">show</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="13"></td><td><pre>        <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="14"></td><td><pre>        viewHolder<span class="token punctuation">.</span>fruitImage<span class="token punctuation">.</span><span class="token function">setOnClickListener</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="15"></td><td><pre>            <span class="token keyword">val</span> position <span class="token operator">=</span> viewHolder<span class="token punctuation">.</span>adapterPosition</pre></td></tr><tr><td data-num="16"></td><td><pre>            <span class="token keyword">val</span> fruit <span class="token operator">=</span> fruitList<span class="token punctuation">[</span>position<span class="token punctuation">]</span></pre></td></tr><tr><td data-num="17"></td><td><pre>            Toast<span class="token punctuation">.</span><span class="token function">makeText</span><span class="token punctuation">(</span>parent<span class="token punctuation">.</span>context<span class="token punctuation">,</span> <span class="token string-literal singleline"><span class="token string">"you clicked image </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">$&#123;</span><span class="token expression">fruit<span class="token punctuation">.</span>name</span><span class="token interpolation-punctuation punctuation">&#125;</span></span><span class="token string">"</span></span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="18"></td><td><pre>                Toast<span class="token punctuation">.</span>LENGTH_SHORT<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">show</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="19"></td><td><pre>        <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="20"></td><td><pre>        <span class="token keyword">return</span> viewHolder</pre></td></tr><tr><td data-num="21"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="22"></td><td><pre>    <span class="token operator">..</span><span class="token punctuation">.</span></pre></td></tr><tr><td data-num="23"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="24"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">129</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>由于手机屏幕的空间一般比较小，有些时候过多的内容一屏是显示不下的，借助 ScrollView 控件，我们就可以以滚动的形式查看屏幕外的内容</p>
</li>
</ul>
<h2 id="需要注意的属性"><a class="markdownIt-Anchor" href="#需要注意的属性">#</a> 需要注意的属性</h2>
<h3 id="visibility"><a class="markdownIt-Anchor" href="#visibility">#</a>  <code>visibility</code></h3>
<ul>
<li>这个属性可以指定控件的可见性</li>
<li>他的可选属性有以下三种
<ul>
<li><code>visible</code>
<ul>
<li>View.VISIBLE</li>
<li>表示控件可见</li>
</ul>
</li>
<li><code>invisible</code>
<ul>
<li>View.INVISIBLE</li>
<li>表示控件不可见，但是占据着原来的大小和位置</li>
</ul>
</li>
<li><code>gone</code>
<ul>
<li>View.GONE</li>
<li>表示空间不可见，而且不再占用任何屏幕空间</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="gravity-和-layout_gravity"><a class="markdownIt-Anchor" href="#gravity-和-layout_gravity">#</a>  <code>gravity</code>  和  <code>layout_gravity</code></h3>
<ul>
<li><code>gravity</code>  是文字在控件内部的对齐方式</li>
<li><code>layout_gravity</code>  是控件在布局内部的对齐方式</li>
</ul>
<h3 id="layout_weight"><a class="markdownIt-Anchor" href="#layout_weight">#</a>  <code>layout_weight</code></h3>
<ul>
<li>系统会先把 Linearlayout 下所有空间指定的 <code>layout_weight</code>  值相加，得到一个总值，然后每个控件所占大小的比例就是使用该控件的 <code>layout_weight</code>  值除以刚才算出的总值</li>
<li>需要注意的是，当 Linearlayout 使用的 <code>vertical</code>  时，水平方向上可以使用 <code>layout_weight</code> ，当使用的是 <code>horizontal</code>  时，垂直方向上可以使用 <code>layout_weight</code> ，另外所指定的方向的 width 或 height 需要指定为<strong> 0dp</strong></li>
</ul>
<h2 id="基本布局"><a class="markdownIt-Anchor" href="#基本布局">#</a> 基本布局</h2>
<ul>
<li>布局是一种可用于放置很多控件的容器，它可以按照一定的规律调整内部控件的位置，从而编写出精美的界面。当然，布局的内部除了放置控件外，也可以放置布局，通过多层布局的嵌套，我们就能够完成一些比较复杂的界面实现
<ul>
<li><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://tuchuang.wenhe9.cn/image-20210924192053333.png"
                      alt="image-20210924192053333"
                ></li>
</ul>
</li>
</ul>
<h3 id="linearlayout"><a class="markdownIt-Anchor" href="#linearlayout">#</a> LinearLayout</h3>
<ul>
<li>Linearlayout 又称为线性布局，这个布局会将他所包含的控件在线性方向上依次排列。</li>
<li>可以通过 <code>orientation</code>  指定布局的方向</li>
</ul>
<h3 id="relativelayout"><a class="markdownIt-Anchor" href="#relativelayout">#</a> RelativeLayout</h3>
<ul>
<li>
<p>相对布局，他可以通过相对定位的方式让控件出现在布局的任何位置</p>
</li>
<li>
<div class="highlight-container" data-rel="Xml"><figure class="iseeu highlight xml"><figcaption data-lang="XML"></figcaption><table><tr><td data-num="1"></td><td><pre>android:layout_alignParentLeft、android:layout_alignParentTop、android:layout_alignParentRight、android:layout_alignParentBottom、android:layout_centerInParent、</pre></td></tr><tr><td data-num="2"></td><td><pre>android:layout_above</pre></td></tr><tr><td data-num="3"></td><td><pre>android:layout_below</pre></td></tr><tr><td data-num="4"></td><td><pre>android:layout_toLeftOf</pre></td></tr><tr><td data-num="5"></td><td><pre>android:layout_toRightOf</pre></td></tr><tr><td data-num="6"></td><td><pre>android:layout_alignLeft</pre></td></tr><tr><td data-num="7"></td><td><pre>android:layout_alignRight</pre></td></tr><tr><td data-num="8"></td><td><pre>android:layout_alignTop</pre></td></tr><tr><td data-num="9"></td><td><pre>android:layout_alignBottom</pre></td></tr><tr><td data-num="10"></td><td><pre><span class="token comment"><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">### FrameLayout</span><br><span class="line"></span><br><span class="line">- 帧布局，所有的控件都会默认摆放在布局的左上角</span><br><span class="line">- 可以使用``layout_gravity``指定对齐方式</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">## 小技巧</span><br><span class="line"></span><br><span class="line">### 隐藏系统自带的标题栏</span><br><span class="line"></span><br><span class="line">- ```kotlin</span><br><span class="line">  class MainActivity : AppCompatActivity() &#123;</span><br><span class="line">  </span><br><span class="line">      override fun onCreate(savedInstanceState: Bundle?) &#123;</span><br><span class="line">          super.onCreate(savedInstanceState)</span><br><span class="line">          setContentView(R.layout.activity_main)</span><br><span class="line">          supportActionBar?.hide()</span><br><span class="line">      &#125;</span><br><span class="line">  </span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure></div></div></span></pre></td></tr></table></figure></li>
</ul>
<h3 id="动态加载布局"><a class="markdownIt-Anchor" href="#动态加载布局">#</a> 动态加载布局</h3>
<ul>
<li>希望程序能够根据设备的分辨率或屏幕大小，在运行时决定加载哪个布局，这就涉及到 Android 中动态加载布局的技巧</li>
</ul>
<h4 id="使用限定符"><a class="markdownIt-Anchor" href="#使用限定符">#</a> 使用限定符</h4>
<ul>
<li>
<p><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://tuchuang.wenhe9.cn/image-20210924212049336.png"
                      alt="image-20210924212049336"
                ></p>
</li>
<li>
<p>示例</p>
<ul>
<li>在 res 下建立一个 layout-large 的文件夹，将我们需要动态加载的布局在这个文件夹下新建一份，那么当屏幕大小满足 large 这个限定符的时候，就会动态的加载这里的布局文件</li>
</ul>
</li>
</ul>
<h4 id="使用最小宽度限定符"><a class="markdownIt-Anchor" href="#使用最小宽度限定符">#</a> 使用最小宽度限定符</h4>
<ul>
<li>虽然上面使用 large 很方便，但是 large 到底是多大呢？有时候我们希望可以更加灵活的为不同设备加载布局，不管他们是不是被系统认定为 large，这时就可以使用最小宽度限定符</li>
<li>最小宽度限定符允许我们对屏幕的宽度制定一个最小值（以 dp 为单位），然后以这个最小值为临界点，屏幕宽度大于这个值的设备就在一个布局，屏幕宽度小于这个值的设备就加载另一个布局</li>
<li>示例：
<ul>
<li>在 res 下建立一个 layout-sw600dp 文件夹，将我们需要动态加载的怒局在这个文件夹下新建一份，当程序运行在屏幕宽度大于等于 600dp 的设备上时，就会加载这个文件夹下的内容，当程序运行在小于 600dp 设备上时，则会加载默认的布局文件</li>
</ul>
</li>
</ul>
<h3 id="加载图片的库"><a class="markdownIt-Anchor" href="#加载图片的库">#</a> 加载图片的库</h3>
<ul>
<li>Glide</li>
</ul>
<h3 id="全局获取context的技巧"><a class="markdownIt-Anchor" href="#全局获取context的技巧">#</a> 全局获取 Context 的技巧</h3>
<ul>
<li>
<p>Android 提供了一个 Application 类，每当应用程序启动的时候，系统就会自动将这个类进行初始化。而我们可以定制一个自己的 Application 类，以便于管理程序内一些全局的状态信息，比如全局 Context。</p>
</li>
<li>
<p>定制一个自己的 Application 其实并不复杂，首先需要创建一个 MyApplication 类继承自 Application，代码如下所示：</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">class</span> MyApplication <span class="token operator">:</span> <span class="token function">Application</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre></pre></td></tr><tr><td data-num="3"></td><td><pre>    <span class="token keyword">companion</span> <span class="token keyword">object</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="4"></td><td><pre>        <span class="token keyword">lateinit</span> <span class="token keyword">var</span> context<span class="token operator">:</span> Context</pre></td></tr><tr><td data-num="5"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="6"></td><td><pre></pre></td></tr><tr><td data-num="7"></td><td><pre>    <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">onCreate</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="8"></td><td><pre>        <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">onCreate</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="9"></td><td><pre>        context <span class="token operator">=</span> applicationContext</pre></td></tr><tr><td data-num="10"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="11"></td><td><pre></pre></td></tr><tr><td data-num="12"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="13"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">131</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>接下来我们还需要告知系统，当程序启动的时候应该初始化 MyApplication 类，而不是默认的 Application 类。这一步也很简单，在 AndroidManifest.xml 文件的 <code>&lt;application&gt;</code>  标签下进行指定就可以了，代码如下所示：</p>
<ul>
<li>
<div class="highlight-container" data-rel="Xml"><figure class="iseeu highlight xml"><figcaption data-lang="XML"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>manifest</span> <span class="token attr-name"><span class="token namespace">xmlns:</span>android</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://schemas.android.com/apk/res/android<span class="token punctuation">"</span></span></pre></td></tr><tr><td data-num="2"></td><td><pre>          <span class="token attr-name">package</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>com.example.materialtest<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="3"></td><td><pre>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>application</span></pre></td></tr><tr><td data-num="4"></td><td><pre>        <span class="token attr-name"><span class="token namespace">android:</span>name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>.MyApplication<span class="token punctuation">"</span></span></pre></td></tr><tr><td data-num="5"></td><td><pre>        <span class="token attr-name"><span class="token namespace">android:</span>allowBackup</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span></pre></td></tr><tr><td data-num="6"></td><td><pre>        <span class="token attr-name"><span class="token namespace">android:</span>icon</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>@mipmap/ic_launcher<span class="token punctuation">"</span></span></pre></td></tr><tr><td data-num="7"></td><td><pre>        <span class="token attr-name"><span class="token namespace">android:</span>label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>@string/app_name<span class="token punctuation">"</span></span></pre></td></tr><tr><td data-num="8"></td><td><pre>        <span class="token attr-name"><span class="token namespace">android:</span>roundIcon</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>@mipmap/ic_launcher_round<span class="token punctuation">"</span></span></pre></td></tr><tr><td data-num="9"></td><td><pre>        <span class="token attr-name"><span class="token namespace">android:</span>supportsRtl</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span></pre></td></tr><tr><td data-num="10"></td><td><pre>        <span class="token attr-name"><span class="token namespace">android:</span>theme</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>@style/AppTheme<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="11"></td><td><pre>        ...</pre></td></tr><tr><td data-num="12"></td><td><pre>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>application</span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="13"></td><td><pre><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>manifest</span><span class="token punctuation">></span></span></pre></td></tr><tr><td data-num="14"></td><td><pre><span class="token comment"><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">- 这样我们就实现了一种全局获取Context的机制，之后不管你想在项目的任何地方使用Context，只需要调用一下MyApplication.context就可以了。</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">### 使用Intent传递对象</span><br><span class="line"></span><br><span class="line">#### Serializables方式</span><br><span class="line"></span><br><span class="line">- Serializable是序列化的意思，表示将一个对象转换成可存储或可传输的状态。序列化后的对象可以在网络上进行传输，也可以存储到本地。至于序列化的方法非常简单，只需要让一个类去实现Serializable这个接口就可以了。</span><br><span class="line"></span><br><span class="line">- 比如说有一个Person类，其中包含了name和age这两个字段，如果想要将它序列化，就可以这样写：</span><br><span class="line"></span><br><span class="line">  - ```kotlin</span><br><span class="line">    class Person : Serializable &#123;</span><br><span class="line">        var name = &quot;&quot;</span><br><span class="line">        var age = 0</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure></div></div></span></pre></td></tr></table></figure></li>
</ul>
</li>
<li>
<p>这里我们让 Person 类实现了 Serializable 接口，这样所有的 Person 对象都是可序列化的了。</p>
</li>
<li>
<p>然后在 FirstActivity 中只需要这样写：</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">val</span> person <span class="token operator">=</span> <span class="token function">Person</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="2"></td><td><pre>person<span class="token punctuation">.</span>name <span class="token operator">=</span> <span class="token string-literal singleline"><span class="token string">"Tom"</span></span></pre></td></tr><tr><td data-num="3"></td><td><pre>person<span class="token punctuation">.</span>age <span class="token operator">=</span> <span class="token number">20</span></pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token keyword">val</span> intent <span class="token operator">=</span> <span class="token function">Intent</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">,</span> SecondActivity<span class="token operator">::</span><span class="token keyword">class</span><span class="token punctuation">.</span>java<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="5"></td><td><pre>intent<span class="token punctuation">.</span><span class="token function">putExtra</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"person_data"</span></span><span class="token punctuation">,</span> person<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="6"></td><td><pre><span class="token function">startActivity</span><span class="token punctuation">(</span>intent<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="7"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">133</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>这里调用了 Intent 的 getSerializableExtra () 方法来获取通过参数传递过来的序列化对象，接着再将它向下转型成 Person 对象，这样我们就成功实现了使用 Intent 传递对象的功能。</p>
</li>
<li>
<p>需要注意的是，这种传递对象的工作原理是先将一个对象序列化成可存储或可传输的状态，传递给另外一个 Activity 后再将其反序列化成一个新的对象。虽然这两个对象中存储的数据完全一致，但是它们实际上是不同的对象，这一点希望你能了解清楚。</p>
</li>
</ul>
<h4 id="parcelable方式"><a class="markdownIt-Anchor" href="#parcelable方式">#</a> Parcelable 方式</h4>
<ul>
<li>
<p>除了 Serializable 之外，使用 Parcelable 也可以实现相同的效果，不过不同于将对象进行序列化，Parcelable 方式的实现原理是将一个完整的对象进行分解，而分解后的每一部分都是 Intent 所支持的数据类型，这样就能实现传递对象的功能了。</p>
</li>
<li>
<p>下面我们来看一下 Parcelable 的实现方式，修改 Person 中的代码，如下所示：</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token keyword">class</span> Person <span class="token operator">:</span> Parcelable <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre>    <span class="token keyword">var</span> name <span class="token operator">=</span> <span class="token string-literal singleline"><span class="token string">""</span></span></pre></td></tr><tr><td data-num="3"></td><td><pre>    <span class="token keyword">var</span> age <span class="token operator">=</span> <span class="token number">0</span></pre></td></tr><tr><td data-num="4"></td><td><pre></pre></td></tr><tr><td data-num="5"></td><td><pre>    <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">writeToParcel</span><span class="token punctuation">(</span>parcel<span class="token operator">:</span> Parcel<span class="token punctuation">,</span> flags<span class="token operator">:</span> Int<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="6"></td><td><pre>        parcel<span class="token punctuation">.</span><span class="token function">writeString</span><span class="token punctuation">(</span>name<span class="token punctuation">)</span> <span class="token comment">// 写出 name</span></pre></td></tr><tr><td data-num="7"></td><td><pre>        parcel<span class="token punctuation">.</span><span class="token function">writeInt</span><span class="token punctuation">(</span>age<span class="token punctuation">)</span> <span class="token comment">// 写出 age</span></pre></td></tr><tr><td data-num="8"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="9"></td><td><pre></pre></td></tr><tr><td data-num="10"></td><td><pre>    <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">describeContents</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">:</span> Int <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="11"></td><td><pre>        <span class="token keyword">return</span> <span class="token number">0</span></pre></td></tr><tr><td data-num="12"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="13"></td><td><pre></pre></td></tr><tr><td data-num="14"></td><td><pre>    <span class="token keyword">companion</span> <span class="token keyword">object</span> CREATOR <span class="token operator">:</span> Parcelable<span class="token punctuation">.</span>Creator<span class="token operator">&lt;</span>Person<span class="token operator">></span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="15"></td><td><pre>        <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">createFromParcel</span><span class="token punctuation">(</span>parcel<span class="token operator">:</span> Parcel<span class="token punctuation">)</span><span class="token operator">:</span> Person <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="16"></td><td><pre>            <span class="token keyword">val</span> person <span class="token operator">=</span> <span class="token function">Person</span><span class="token punctuation">(</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="17"></td><td><pre>            person<span class="token punctuation">.</span>name <span class="token operator">=</span> parcel<span class="token punctuation">.</span><span class="token function">readString</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">?:</span> <span class="token string-literal singleline"><span class="token string">""</span></span> <span class="token comment">// 读取 name</span></pre></td></tr><tr><td data-num="18"></td><td><pre>            person<span class="token punctuation">.</span>age <span class="token operator">=</span> parcel<span class="token punctuation">.</span><span class="token function">readInt</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 读取 age</span></pre></td></tr><tr><td data-num="19"></td><td><pre>            <span class="token keyword">return</span> person</pre></td></tr><tr><td data-num="20"></td><td><pre>        <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="21"></td><td><pre></pre></td></tr><tr><td data-num="22"></td><td><pre>        <span class="token keyword">override</span> <span class="token keyword">fun</span> <span class="token function">newArray</span><span class="token punctuation">(</span>size<span class="token operator">:</span> Int<span class="token punctuation">)</span><span class="token operator">:</span> Array<span class="token operator">&lt;</span>Person<span class="token operator">?</span><span class="token operator">></span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="23"></td><td><pre>            <span class="token keyword">return</span> <span class="token function">arrayOfNulls</span><span class="token punctuation">(</span>size<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="24"></td><td><pre>        <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="25"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="26"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="27"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">134</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>注意，这里不再是调用 getSerializableExtra () 方法，而是调用 getParcelableExtra () 方法来获取传递过来的对象，其他的地方完全相同。</p>
</li>
<li>
<p>不过，这种实现方式写起来确实比较复杂，为此 Kotlin 给我们提供了另外一种更加简便的用法，但前提是要传递的所有数据都必须封装在对象的主构造函数中才行。</p>
</li>
<li>
<p>修改 Person 类中的代码，如下所示：</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre><span class="token annotation builtin">@Parcelize</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token keyword">class</span> <span class="token function">Person</span><span class="token punctuation">(</span><span class="token keyword">var</span> name<span class="token operator">:</span> String<span class="token punctuation">,</span> <span class="token keyword">var</span> age<span class="token operator">:</span> Int<span class="token punctuation">)</span> <span class="token operator">:</span> Parcelable</pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">135</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>可以看到，我们在 LogUtil 中首先定义了 VERBOSE、DEBUG、INFO、WARN、ERROR 这 5 个整型常量，并且它们对应的值都是递增的。然后又定义了一个静态变量 level，可以将它的值指定为上面 5 个常量中的任意一个。</p>
</li>
<li>
<p>接下来，我们提供了 v ()、d ()、i ()、w ()、e () 这 5 个自定义的日志方法，在其内部分别调用了 Log.v ()、Log.d ()、Log.i ()、Log.w ()、Log.e () 这 5 个方法来打印日志，只不过在这些自定义的方法中都加入了一个 if 判断，只有当 level 的值小于或等于对应日志级别值的时候，才会将日志打印出来。</p>
</li>
<li>
<p>这样就把一个自定义的日志工具创建好了，之后在项目里，我们可以像使用普通的日志工具一样使用 LogUtil。比如打印一行 DEBUG 级别的日志可以这样写：</p>
<ul>
<li>
<div class="highlight-container" data-rel="Kotlin"><figure class="iseeu highlight kotlin"><figcaption data-lang="kotlin"></figcaption><table><tr><td data-num="1"></td><td><pre>LogUtil<span class="token punctuation">.</span><span class="token function">d</span><span class="token punctuation">(</span><span class="token string-literal singleline"><span class="token string">"TAG"</span></span><span class="token punctuation">,</span> <span class="token string-literal singleline"><span class="token string">"debug log"</span></span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span>code￼<span class="token number">136</span><span class="token operator">--</span><span class="token operator">></span></pre></td></tr></table></figure></div></li>
</ul>
</li>
<li>
<p>我们只需要通过修改 level 变量的值，就可以自由地控制日志的打印行为。比如让 level 等于 VERBOSE 就可以把所有的日志都打印出来，让 level 等于 ERROR 就可以只打印程序的错误日志。</p>
</li>
<li>
<p>使用了这种方法之后，刚才所说的那个问题也就不复存在了，你只需要在开发阶段将 level 指定成 VERBOSE，当项目正式上线的时候将 level 指定成 ERROR 就可以了。</p>
</li>
</ul>

            </div>

            
                <div class="post-copyright-info">
                    <div class="article-copyright-info-container">
    <ul>
        <li><strong>标题:</strong> Android</li>
        <li><strong>作者:</strong> meteor</li>
        <li><strong>创建于:</strong> 2023-06-01 12:37:34</li>
        
            <li>
                <strong>更新于:</strong> 2023-06-05 00:25:35
            </li>
        
        <li>
            <strong>链接:</strong> https://gitee.com/du-jinliang/2023/06/02/Android/
        </li>
        <li>
            <strong>版权声明:</strong> 本文章采用 <a class="license" target="_blank" rel="noopener" href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh">CC BY-NC-SA 4.0</a> 进行许可。
        </li>
    </ul>
</div>

                </div>
            

            
                <ul class="post-tags-box">
                    
                        <li class="tag-item">
                            <a href="/tags/Android/">#Android</a>&nbsp;
                        </li>
                    
                </ul>
            

            

            
                <div class="article-nav">
                    
                        <div class="article-prev">
                            <a class="prev"
                            rel="prev"
                            href="/2023/06/02/RabbitMQ/"
                            >
                                <span class="left arrow-icon flex-center">
                                    <i class="fa-solid fa-chevron-left"></i>
                                </span>
                                <span class="title flex-center">
                                    <span class="post-nav-title-item">RabbitMQ</span>
                                    <span class="post-nav-item">上一篇</span>
                                </span>
                            </a>
                        </div>
                    
                    
                </div>
            


            
                <div class="comment-container">
                    <div class="comments-container">
    <div id="comment-anchor"></div>
    <div class="comment-area-title">
        <i class="fa-solid fa-comments"></i>&nbsp;评论
    </div>
    

        
            
    <div id="gitalk-container"></div>
    <script data-pjax
            src="//cdn.jsdelivr.net/npm/gitalk/dist/gitalk.min.js"></script>
    <script data-pjax>

        function loadGitalk() {
            let __gitalk__pathname = decodeURI(location.pathname);
            const __gitalk__pathnameLength = __gitalk__pathname.length;
            const __gitalk__pathnameMaxLength = 50;
            if (__gitalk__pathnameLength > __gitalk__pathnameMaxLength) {
                __gitalk__pathname = __gitalk__pathname.substring(0, __gitalk__pathnameMaxLength - 3) + '...';
            }

            try {
                Gitalk && new Gitalk({
                    clientID: '55bad54a77b7e60ad62d',
                    clientSecret: '1031c81500c6be06e338087cb7b713f2d0201b46',
                    repo: 'wait-you.github.io',
                    owner: 'wait-you',
                    admin: ['wait-you'],
                    id: 'comment',
                    language: 'zh-CN'
                }).render('gitalk-container');

            } catch (e) {
                window.Gitalk = null;
            }
        }

        if ('true') {
            const loadGitalkTimeout = setTimeout(() => {
                loadGitalk();
                clearTimeout(loadGitalkTimeout);
            }, 1000);
        } else {
            window.addEventListener('DOMContentLoaded', loadGitalk);
        }
    </script>



        
    
</div>

                </div>
            
        </div>

        
            <div class="toc-content-container">
                <div class="post-toc-wrap">
    <div class="post-toc">
        <div class="toc-title">此页目录</div>
        <div class="page-title">Android</div>
        <ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#android"><span class="nav-text"> Android</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#%E7%B3%BB%E7%BB%9F%E6%9E%B6%E6%9E%84"><span class="nav-text"> 系统架构</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#linux%E5%86%85%E6%A0%B8%E5%B1%82"><span class="nav-text"> Linux 内核层</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%A1%AC%E4%BB%B6%E6%8A%BD%E8%B1%A1%E5%B1%82hal"><span class="nav-text"> 硬件抽象层（HAL）</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%B3%BB%E7%BB%9F%E8%BF%90%E8%A1%8C%E5%BA%93%E5%B1%82"><span class="nav-text"> 系统运行库层</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#java-api-%E6%A1%86%E6%9E%B6"><span class="nav-text"> JAVA API 框架</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%B3%BB%E7%BB%9F%E5%BA%94%E7%94%A8"><span class="nav-text"> 系统应用</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%BA%94%E7%94%A8%E5%BC%80%E5%8F%91%E7%89%B9%E8%89%B2"><span class="nav-text"> 应用开发特色</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%9B%9B%E5%A4%A7%E7%BB%84%E4%BB%B6"><span class="nav-text"> 四大组件</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E4%B8%B0%E5%AF%8C%E7%9A%84%E7%B3%BB%E7%BB%9F%E6%8E%A7%E4%BB%B6"><span class="nav-text"> 丰富的系统控件</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#sqlite%E6%95%B0%E6%8D%AE%E5%BA%93"><span class="nav-text"> SQLite 数据库</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%BC%BA%E5%A4%A7%E7%9A%84%E5%A4%9A%E5%AA%92%E4%BD%93"><span class="nav-text"> 强大的多媒体</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E9%A1%B9%E7%9B%AE%E7%BB%93%E6%9E%84"><span class="nav-text"> 项目结构</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E9%A1%B9%E7%9B%AE%E4%B8%AD%E7%9A%84%E8%B5%84%E6%BA%90"><span class="nav-text"> 项目中的资源</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%88%9B%E5%BB%BA%E5%92%8C%E5%8A%A0%E8%BD%BD%E5%B8%83%E5%B1%80"><span class="nav-text"> 创建和加载布局</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%9C%A8activity%E4%B8%AD%E4%BD%BF%E7%94%A8toast"><span class="nav-text"> 在 Activity 中使用 Toast</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E9%94%80%E6%AF%81%E4%B8%80%E4%B8%AAactivity"><span class="nav-text"> 销毁一个 Activity</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#activity%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F"><span class="nav-text"> Activity 的生命周期</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#activity%E7%9A%84%E5%90%AF%E5%8A%A8%E6%A8%A1%E5%BC%8F"><span class="nav-text"> Activity 的启动模式</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#fragment"><span class="nav-text"> Fragment</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E9%9D%99%E6%80%81%E6%B7%BB%E5%8A%A0fragment"><span class="nav-text"> 静态添加 Fragment</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%8A%A8%E6%80%81%E5%BC%95%E5%85%A5fragment"><span class="nav-text"> 动态引入 Fragment</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#fragment%E5%92%8Cactivity%E4%B9%8B%E9%97%B4%E7%9A%84%E4%BA%A4%E4%BA%92"><span class="nav-text"> Fragment 和 Activity 之间的交互</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E6%8C%81%E4%B9%85%E5%8C%96%E6%8A%80%E6%9C%AF"><span class="nav-text"> 持久化技术</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%96%87%E4%BB%B6%E5%AD%98%E5%82%A8"><span class="nav-text"> 文件存储</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#sharedpreferences%E5%AD%98%E5%82%A8"><span class="nav-text"> SharedPreferences 存储</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#sqlite%E6%95%B0%E6%8D%AE%E5%BA%93%E5%AD%98%E5%82%A8"><span class="nav-text"> SQLite 数据库存储</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E8%BF%90%E8%A1%8C%E6%97%B6%E6%9D%83%E9%99%90"><span class="nav-text"> 运行时权限</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#android%E6%9D%83%E9%99%90%E6%9C%BA%E5%88%B6%E8%AF%A6%E8%A7%A3"><span class="nav-text"> Android 权限机制详解</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#contentprovider"><span class="nav-text"> ContentProvider</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E8%AE%BF%E9%97%AE%E5%85%B6%E4%BB%96%E7%A8%8B%E5%BA%8F%E4%B8%AD%E7%9A%84%E6%95%B0%E6%8D%AE"><span class="nav-text"> 访问其他程序中的数据</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#contentresolver%E7%9A%84%E5%9F%BA%E6%9C%AC%E7%94%A8%E6%B3%95"><span class="nav-text"> ContentResolver 的基本用法</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#service%E5%9F%BA%E6%9C%AC%E7%94%A8%E6%B3%95"><span class="nav-text"> Service 基本用法</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%A4%9A%E5%AA%92%E4%BD%93"><span class="nav-text"> 多媒体</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E4%BD%BF%E7%94%A8%E9%80%9A%E7%9F%A5"><span class="nav-text"> 使用通知</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E8%B0%83%E7%94%A8%E6%91%84%E5%83%8F%E5%A4%B4%E5%92%8C%E7%9B%B8%E5%86%8C"><span class="nav-text"> 调用摄像头和相册</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%92%AD%E6%94%BE%E5%A4%9A%E5%AA%92%E4%BD%93%E6%96%87%E4%BB%B6"><span class="nav-text"> 播放多媒体文件</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E7%BD%91%E7%BB%9C%E6%8A%80%E6%9C%AF"><span class="nav-text"> 网络技术</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#webview%E7%9A%84%E7%94%A8%E6%B3%95"><span class="nav-text"> WebView 的用法</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%BD%91%E7%BB%9C%E5%BA%93retrofit"><span class="nav-text"> 网络库 Retrofit</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#material-design"><span class="nav-text"> Material Design</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#toolbar"><span class="nav-text"> Toolbar</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#drawerlayout"><span class="nav-text"> DrawerLayout</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#navigationview"><span class="nav-text"> NavigationView</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#floatingactionbutton"><span class="nav-text"> FloatingActionButton</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#snackbar"><span class="nav-text"> Snackbar</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#coordinatorlayout"><span class="nav-text"> CoordinatorLayout</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%8D%A1%E7%89%87%E5%BC%8F%E5%B8%83%E5%B1%80"><span class="nav-text"> 卡片式布局</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E4%B8%8B%E6%8B%89%E5%88%B7%E6%96%B0"><span class="nav-text"> 下拉刷新</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%8F%AF%E6%8A%98%E5%8F%A0%E5%BC%8F%E6%A0%87%E9%A2%98%E6%A0%8F"><span class="nav-text"> 可折叠式标题栏</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#jetpack"><span class="nav-text"> Jetpack</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#viewmodel"><span class="nav-text"> ViewModel</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#lifecycles"><span class="nav-text"> LifeCycles</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#button"><span class="nav-text"> Button</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#alertdialog"><span class="nav-text"> AlertDialog</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#recyclerview"><span class="nav-text"> RecyclerView</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E9%9C%80%E8%A6%81%E6%B3%A8%E6%84%8F%E7%9A%84%E5%B1%9E%E6%80%A7"><span class="nav-text"> 需要注意的属性</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#visibility"><span class="nav-text">  visibility</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#gravity-%E5%92%8C-layout_gravity"><span class="nav-text">  gravity  和  layout_gravity</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#layout_weight"><span class="nav-text">  layout_weight</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%9F%BA%E6%9C%AC%E5%B8%83%E5%B1%80"><span class="nav-text"> 基本布局</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#linearlayout"><span class="nav-text"> LinearLayout</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#relativelayout"><span class="nav-text"> RelativeLayout</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%8A%A8%E6%80%81%E5%8A%A0%E8%BD%BD%E5%B8%83%E5%B1%80"><span class="nav-text"> 动态加载布局</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%8A%A0%E8%BD%BD%E5%9B%BE%E7%89%87%E7%9A%84%E5%BA%93"><span class="nav-text"> 加载图片的库</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%85%A8%E5%B1%80%E8%8E%B7%E5%8F%96context%E7%9A%84%E6%8A%80%E5%B7%A7"><span class="nav-text"> 全局获取 Context 的技巧</span></a></li></ol></li></ol></li></ol>

    </div>
</div>
            </div>
        
    </div>
</div>


                

            </div>
            
            

        </div>

        <div class="main-content-footer">
            <footer class="footer">
    <div class="info-container">
        <div class="copyright-info">
            &copy;
            
              <span>2022</span>
              -
            
            2023&nbsp;&nbsp;<i class="fa-solid fa-heart fa-beat" style="--fa-animation-duration: 0.5s; color: #f54545"></i>&nbsp;&nbsp;<a href="/">meteor</a>
        </div>
        
            <script async data-pjax src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>
            <div class="website-count info-item">
                
                    <span id="busuanzi_container_site_uv" class="busuanzi_container_site_uv">
                        访问人数&nbsp;<span id="busuanzi_value_site_uv" class="busuanzi_value_site_uv"></span>
                    </span>
                
                
                    <span id="busuanzi_container_site_pv" class="busuanzi_container_site_pv">
                        总访问量&nbsp;<span id="busuanzi_value_site_pv" class="busuanzi_value_site_pv"></span>
                    </span>
                
            </div>
        
        <div class="theme-info info-item">
            <span class="powered-by-container">由 <?xml version="1.0" encoding="utf-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg version="1.1" id="圖層_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="1rem" height="1rem" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve"><path fill="#0E83CD" d="M256.4,25.8l-200,115.5L56,371.5l199.6,114.7l200-115.5l0.4-230.2L256.4,25.8z M349,354.6l-18.4,10.7l-18.6-11V275H200v79.6l-18.4,10.7l-18.6-11v-197l18.5-10.6l18.5,10.8V237h112v-79.6l18.5-10.6l18.5,10.8V354.6z"/></svg><a target="_blank" href="https://hexo.io">Hexo</a> 驱动</span>
                <br>
            <span class="theme-version-container">主题&nbsp;<a class="theme-version" target="_blank" href="https://github.com/EvanNotFound/hexo-theme-redefine">Redefine v2.1.4</a>
        </div>
        
            <div class="icp-info info-item"><a target="_blank" rel="nofollow" href="
                
                    beian.miit.gov.cn
                
                ">冀ICP备20010108号</a></div>
        
        
        
            <div id="start_div" style="display:none">
                2022/8/17 11:45:14
            </div>
            <div>
                博客已运行 <span class="odometer" id="runtime_days" ></span> 天 <span class="odometer" id="runtime_hours"></span> 小时 <span class="odometer" id="runtime_minutes"></span> 分钟 <span class="odometer" id="runtime_seconds"></span> 秒
            </div>
        
        
        
            <script async data-pjax>
                try {
                    function odometer_init() {
                    const elements = document.querySelectorAll('.odometer');
                    elements.forEach(el => {
                        new Odometer({
                            el,
                            format: '( ddd).dd',
                            duration: 200
                        });
                    });
                    }
                    odometer_init();
                } catch (error) {}
            </script>
        
        
        
    </div>  
</footer>
        </div>
    </div>

    
        <div class="post-tools">
            <div class="post-tools-container">
    <ul class="article-tools-list">
        <!-- TOC aside toggle -->
        
            <li class="right-bottom-tools page-aside-toggle">
                <i class="fa-regular fa-outdent"></i>
            </li>
        

        <!-- go comment -->
        
            <li class="go-comment">
                <i class="fa-regular fa-comments"></i>
            </li>
        
    </ul>
</div>

        </div>
    

    <div class="right-side-tools-container">
        <div class="side-tools-container">
    <ul class="hidden-tools-list">
        <li class="right-bottom-tools tool-font-adjust-plus flex-center">
            <i class="fa-regular fa-magnifying-glass-plus"></i>
        </li>

        <li class="right-bottom-tools tool-font-adjust-minus flex-center">
            <i class="fa-regular fa-magnifying-glass-minus"></i>
        </li>

        <li class="right-bottom-tools tool-expand-width flex-center">
            <i class="fa-regular fa-expand"></i>
        </li>

        <li class="right-bottom-tools tool-dark-light-toggle flex-center">
            <i class="fa-regular fa-moon"></i>
        </li>

        <!-- rss -->
        

        

        <li class="right-bottom-tools tool-scroll-to-bottom flex-center">
            <i class="fa-regular fa-arrow-down"></i>
        </li>
    </ul>

    <ul class="visible-tools-list">
        <li class="right-bottom-tools toggle-tools-list flex-center">
            <i class="fa-regular fa-cog fa-spin"></i>
        </li>
        
            <li class="right-bottom-tools tool-scroll-to-top flex-center">
                <i class="arrow-up fas fa-arrow-up"></i>
                <span class="percent"></span>
            </li>
        
        
    </ul>
</div>

    </div>

    <div class="image-viewer-container">
    <img src="">
</div>


    


</main>




<script src="/js/utils.js"></script>

<script src="/js/main.js"></script>

<script src="/js/layouts/navbarShrink.js"></script>

<script src="/js/tools/scrollTopBottom.js"></script>

<script src="/js/tools/lightDarkSwitch.js"></script>





    
<script src="/js/tools/codeBlock.js"></script>




    
<script src="/js/layouts/lazyload.js"></script>




    
<script src="/js/tools/runtime.js"></script>

    
<script src="/js/layouts/odometer.min.js"></script>

    
<link rel="stylesheet" href="/assets/odometer-theme-minimal.css">




  
<script src="/js/libs/Typed.min.js"></script>

  
<script src="/js/plugins/typed.js"></script>







<div class="post-scripts pjax">
    
        
<script src="/js/tools/tocToggle.js"></script>

<script src="/js/libs/anime.min.js"></script>

<script src="/js/layouts/toc.js"></script>

<script src="/js/plugins/tabs.js"></script>

    
</div>


    
<script src="/js/libs/pjax.min.js"></script>

<script>
    window.addEventListener('DOMContentLoaded', () => {
        window.pjax = new Pjax({
            selectors: [
                'head title',
                '.page-container',
                '.pjax',
            ],
            history: true,
            debug: false,
            cacheBust: false,
            timeout: 0,
            analytics: false,
            currentUrlFullReload: false,
            scrollRestoration: false,
            // scrollTo: true,
        });

        document.addEventListener('pjax:send', () => {
            Global.utils.pjaxProgressBarStart();
        });

        document.addEventListener('pjax:complete', () => {
            Global.utils.pjaxProgressBarEnd();
            window.pjax.executeScripts(document.querySelectorAll('script[data-pjax], .pjax script'));
            Global.refresh();
        });
    });
</script>




</body>
</html>
